From dbb04b0d29f884dbb6d0a52dd7877445d2e776f1 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 18 Aug 2015 12:07:48 +0300 Subject: iwlwifi: mvm: remove netdetect debugfs entry This debugfs entry was implemented just as a proof-of-concept before the full support for netdetect via cfg80211/mac80211 was implemented. Now that we have a proper way to enable netdetect from userspace, this entry is redundant and should be removed to avoid unnecessary maintenance work. Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index 7d69a55..08d2ae3 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -1214,118 +1214,6 @@ static ssize_t iwl_dbgfs_d3_sram_read(struct file *file, char __user *user_buf, return ret; } - -#define MAX_NUM_ND_MATCHSETS 10 - -static ssize_t iwl_dbgfs_netdetect_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - const char *seps = ",\n"; - char *buf_ptr = buf; - char *value_str = NULL; - int ret, i; - - /* TODO: don't free if write is being called several times in one go */ - if (mvm->nd_config) { - kfree(mvm->nd_config->match_sets); - kfree(mvm->nd_config); - mvm->nd_config = NULL; - } - - mvm->nd_config = kzalloc(sizeof(*mvm->nd_config) + - (11 * sizeof(struct ieee80211_channel *)), - GFP_KERNEL); - if (!mvm->nd_config) { - ret = -ENOMEM; - goto out_free; - } - - mvm->nd_config->n_channels = 11; - mvm->nd_config->scan_width = NL80211_BSS_CHAN_WIDTH_20; - mvm->nd_config->interval = 5; - mvm->nd_config->min_rssi_thold = -80; - for (i = 0; i < mvm->nd_config->n_channels; i++) - mvm->nd_config->channels[i] = &mvm->nvm_data->channels[i]; - - mvm->nd_config->match_sets = - kcalloc(MAX_NUM_ND_MATCHSETS, - sizeof(*mvm->nd_config->match_sets), - GFP_KERNEL); - if (!mvm->nd_config->match_sets) { - ret = -ENOMEM; - goto out_free; - } - - while ((value_str = strsep(&buf_ptr, seps)) && - strlen(value_str)) { - struct cfg80211_match_set *set; - - if (mvm->nd_config->n_match_sets >= MAX_NUM_ND_MATCHSETS) { - ret = -EINVAL; - goto out_free; - } - - set = &mvm->nd_config->match_sets[mvm->nd_config->n_match_sets]; - set->ssid.ssid_len = strlen(value_str); - - if (set->ssid.ssid_len > IEEE80211_MAX_SSID_LEN) { - ret = -EINVAL; - goto out_free; - } - - memcpy(set->ssid.ssid, value_str, set->ssid.ssid_len); - - mvm->nd_config->n_match_sets++; - } - - ret = count; - - if (mvm->nd_config->n_match_sets) - goto out; - -out_free: - if (mvm->nd_config) - kfree(mvm->nd_config->match_sets); - kfree(mvm->nd_config); - mvm->nd_config = NULL; -out: - return ret; -} - -static ssize_t -iwl_dbgfs_netdetect_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - size_t bufsz, ret; - char *buf; - int i, n_match_sets, pos = 0; - - n_match_sets = mvm->nd_config ? mvm->nd_config->n_match_sets : 0; - - bufsz = n_match_sets * (IEEE80211_MAX_SSID_LEN + 1) + 1; - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - for (i = 0; i < n_match_sets; i++) { - if (pos + - mvm->nd_config->match_sets[i].ssid.ssid_len + 2 > bufsz) { - ret = -EIO; - goto out; - } - - memcpy(buf + pos, mvm->nd_config->match_sets[i].ssid.ssid, - mvm->nd_config->match_sets[i].ssid.ssid_len); - pos += mvm->nd_config->match_sets[i].ssid.ssid_len; - buf[pos++] = '\n'; - } - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); -out: - kfree(buf); - return ret; -} #endif #define PRINT_MVM_REF(ref) do { \ @@ -1503,7 +1391,6 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters_macs, 256); #ifdef CONFIG_PM_SLEEP MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram, 8); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(netdetect, 384); #endif int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) @@ -1572,7 +1459,6 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) if (!debugfs_create_u32("last_netdetect_scans", S_IRUSR, mvm->debugfs_dir, &mvm->last_netdetect_scans)) goto err; - MVM_DEBUGFS_ADD_FILE(netdetect, mvm->debugfs_dir, S_IRUSR | S_IWUSR); #endif if (!debugfs_create_u8("low_latency_agg_frame_limit", S_IRUSR | S_IWUSR, -- cgit v0.10.2 From 62e004fe941d56cb70ea9148bb79cf6698751248 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 20 Aug 2015 14:12:58 +0300 Subject: iwlwifi: mvm: fix statistics variables type When receiving statistics notification there is a field of average energy. This is defines as signed 8 bit, while FW refers to it as unsigned. when the energy is higher than 127 this causes in iwl_mvm_stat_iterator a wrong computation of the signal int sig = -stats->general.beacon_filter_average_energy; resulting in incorrect CQM event (change from low to high). While at it - update the rest of the fields to the correct type. Signed-off-by: Sara Sharon Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h index 709e28d..4ca4bce 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h @@ -237,10 +237,10 @@ struct mvm_statistics_general_v5 { __le32 num_of_sos_states; __le32 beacon_filtered; __le32 missed_beacons; - __s8 beacon_filter_average_energy; - __s8 beacon_filter_reason; - __s8 beacon_filter_current_energy; - __s8 beacon_filter_reserved; + u8 beacon_filter_average_energy; + u8 beacon_filter_reason; + u8 beacon_filter_current_energy; + u8 beacon_filter_reserved; __le32 beacon_filter_delta_time; struct mvm_statistics_bt_activity bt_activity; } __packed; /* STATISTICS_GENERAL_API_S_VER_5 */ @@ -263,10 +263,10 @@ struct mvm_statistics_general_v8 { __le32 num_of_sos_states; __le32 beacon_filtered; __le32 missed_beacons; - __s8 beacon_filter_average_energy; - __s8 beacon_filter_reason; - __s8 beacon_filter_current_energy; - __s8 beacon_filter_reserved; + u8 beacon_filter_average_energy; + u8 beacon_filter_reason; + u8 beacon_filter_current_energy; + u8 beacon_filter_reserved; __le32 beacon_filter_delta_time; struct mvm_statistics_bt_activity bt_activity; __le64 rx_time; diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index b95a07e..72cd67e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -323,11 +323,11 @@ enum iwl_bt_force_ant_mode { struct iwl_mvm_vif_bf_data { bool bf_enabled; bool ba_enabled; - s8 ave_beacon_signal; - s8 last_cqm_event; - s8 bt_coex_min_thold; - s8 bt_coex_max_thold; - s8 last_bt_coex_event; + int ave_beacon_signal; + int last_cqm_event; + int bt_coex_min_thold; + int bt_coex_max_thold; + int last_bt_coex_event; }; /** diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c index c37c10a..2ad814d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/iwlwifi/mvm/rx.c @@ -459,7 +459,7 @@ static void iwl_mvm_update_rx_statistics(struct iwl_mvm *mvm, struct iwl_mvm_stat_data { struct iwl_mvm *mvm; __le32 mac_id; - __s8 beacon_filter_average_energy; + u8 beacon_filter_average_energy; struct mvm_statistics_general_v8 *general; }; -- cgit v0.10.2 From e50460192e6e326fc748037c7ba69565fc18f992 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 17 Aug 2015 10:45:50 +0300 Subject: iwlwifi: mvm: add debugfs hook to send ECHO_CMD to the firmware ECHO_CMD is a simple command that can be sent to the firmware just to check that it is alive. This command is useful for debug purpose, provide a debugfs hook to send it. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index 08d2ae3..3b8481f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -1361,11 +1361,25 @@ out: return count; } +static ssize_t +iwl_dbgfs_send_echo_cmd_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + int ret; + + mutex_lock(&mvm->mutex); + ret = iwl_mvm_send_cmd_pdu(mvm, ECHO_CMD, 0, 0, NULL); + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + MVM_DEBUGFS_READ_WRITE_FILE_OPS(prph_reg, 64); /* Device wide debugfs entries */ MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush, 16); MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain, 8); +MVM_DEBUGFS_WRITE_FILE_OPS(send_echo_cmd, 8); MVM_DEBUGFS_READ_WRITE_FILE_OPS(sram, 64); MVM_DEBUGFS_READ_WRITE_FILE_OPS(set_nic_temperature, 64); MVM_DEBUGFS_READ_FILE_OPS(nic_temp); @@ -1425,6 +1439,7 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) MVM_DEBUGFS_ADD_FILE(d0i3_refs, mvm->debugfs_dir, S_IRUSR | S_IWUSR); MVM_DEBUGFS_ADD_FILE(fw_dbg_conf, mvm->debugfs_dir, S_IRUSR | S_IWUSR); MVM_DEBUGFS_ADD_FILE(fw_dbg_collect, mvm->debugfs_dir, S_IWUSR); + MVM_DEBUGFS_ADD_FILE(send_echo_cmd, mvm->debugfs_dir, S_IWUSR); if (!debugfs_create_bool("enable_scan_iteration_notif", S_IRUSR | S_IWUSR, mvm->debugfs_dir, diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index 4af7513a..cd293d2 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h @@ -100,6 +100,7 @@ enum iwl_mvm_tx_fifo { enum { MVM_ALIVE = 0x1, REPLY_ERROR = 0x2, + ECHO_CMD = 0x3, INIT_COMPLETE_NOTIF = 0x4, diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index a37de3f..0092286 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -271,6 +271,7 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { static const char *const iwl_mvm_cmd_strings[REPLY_MAX + 1] = { CMD(MVM_ALIVE), CMD(REPLY_ERROR), + CMD(ECHO_CMD), CMD(INIT_COMPLETE_NOTIF), CMD(PHY_CONTEXT_CMD), CMD(MGMT_MCAST_KEY), -- cgit v0.10.2 From a73a2cea922de5d2dd03ad456e280ef92c8bb9f6 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 23 Aug 2015 13:29:36 +0300 Subject: iwlwifi: Deinline iwl_{read,write}(8,32} With CONFIG_IWLWIFI_DEVICE_TRACING=y, these functions are rather large, too big for inlining. With this .config: http://busybox.net/~vda/kernel_config, after uninlining these functions have sizes and callsite counts as follows: iwl_read32 475 bytes, 51 callsites iwl_write32 477 bytes, 90 callsites iwl_write8 493 bytes, 3 callsites Reduction in size is about 74,000 bytes: text data bss dec hex filename 90758147 17226024 36659200 144643371 89f152b vmlinux0 90687995 17221928 36659200 144569123 89df323 vmlinux.after Reported-by: Denys Vlasenko Signed-off-by: Emmanuel Grumbach Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/iwlwifi/iwl-io.c b/drivers/net/wireless/iwlwifi/iwl-io.c index 27c66e4..3c263a1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/iwlwifi/iwl-io.c @@ -36,6 +36,28 @@ #include "iwl-prph.h" #include "iwl-fh.h" +void iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val) +{ + trace_iwlwifi_dev_iowrite8(trans->dev, ofs, val); + iwl_trans_write8(trans, ofs, val); +} +IWL_EXPORT_SYMBOL(iwl_write8); + +void iwl_write32(struct iwl_trans *trans, u32 ofs, u32 val) +{ + trace_iwlwifi_dev_iowrite32(trans->dev, ofs, val); + iwl_trans_write32(trans, ofs, val); +} +IWL_EXPORT_SYMBOL(iwl_write32); + +u32 iwl_read32(struct iwl_trans *trans, u32 ofs) +{ + u32 val = iwl_trans_read32(trans, ofs); + trace_iwlwifi_dev_ioread32(trans->dev, ofs, val); + return val; +} +IWL_EXPORT_SYMBOL(iwl_read32); + #define IWL_POLL_INTERVAL 10 /* microseconds */ int iwl_poll_bit(struct iwl_trans *trans, u32 addr, diff --git a/drivers/net/wireless/iwlwifi/iwl-io.h b/drivers/net/wireless/iwlwifi/iwl-io.h index 705d12c..501d056 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.h +++ b/drivers/net/wireless/iwlwifi/iwl-io.h @@ -32,24 +32,9 @@ #include "iwl-devtrace.h" #include "iwl-trans.h" -static inline void iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val) -{ - trace_iwlwifi_dev_iowrite8(trans->dev, ofs, val); - iwl_trans_write8(trans, ofs, val); -} - -static inline void iwl_write32(struct iwl_trans *trans, u32 ofs, u32 val) -{ - trace_iwlwifi_dev_iowrite32(trans->dev, ofs, val); - iwl_trans_write32(trans, ofs, val); -} - -static inline u32 iwl_read32(struct iwl_trans *trans, u32 ofs) -{ - u32 val = iwl_trans_read32(trans, ofs); - trace_iwlwifi_dev_ioread32(trans->dev, ofs, val); - return val; -} +void iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val); +void iwl_write32(struct iwl_trans *trans, u32 ofs, u32 val); +u32 iwl_read32(struct iwl_trans *trans, u32 ofs); static inline void iwl_set_bit(struct iwl_trans *trans, u32 reg, u32 mask) { -- cgit v0.10.2 From 9493908095a3d0b4fa24ff529975376b3a2b8394 Mon Sep 17 00:00:00 2001 From: Gregory Greenman Date: Mon, 24 Aug 2015 14:38:35 +0300 Subject: iwlwifi: mvm: don't ask for beacons when AP vif and no assoc sta When in AP mode, we need beacons from other APs for HT protection. However, when there's no any associated station we will not do any Tx and thus don't really need beacons. On the other hand, these beacons will cause a lot of unnecessary wakeups which increase our power consumption. Handle this by asking FW to pass beacons only when there's at least one associated station. Signed-off-by: Gregory Greenman Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index 3424315..5af0090 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH * * 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 @@ -33,6 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -1124,10 +1126,11 @@ static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm, ctxt_ap->beacon_template = cpu_to_le32(mvmvif->id); } -static int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 action) +int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 action) { + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mac_ctx_cmd cmd = {}; WARN_ON(vif->type != NL80211_IFTYPE_AP || vif->p2p); @@ -1137,10 +1140,16 @@ static int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm, /* * pass probe requests and beacons from other APs (needed - * for ht protection) + * for ht protection); when there're no any associated station + * don't ask FW to pass beacons to prevent unnecessary wake-ups. */ - cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST | - MAC_FILTER_IN_BEACON); + cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST); + if (mvmvif->ap_assoc_sta_count) { + cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON); + IWL_DEBUG_HC(mvm, "Asking FW to pass beacons\n"); + } else { + IWL_DEBUG_HC(mvm, "No need to receive beacons\n"); + } /* Fill the data specific for ap mode */ iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.ap, diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index aa8c2b7..f9f88e9 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -2319,6 +2319,8 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, if (vif->type == NL80211_IFTYPE_AP) iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif); + mvmvif->ap_assoc_sta_count = 0; + /* Add the mac context */ ret = iwl_mvm_mac_ctxt_add(mvm, vif); if (ret) @@ -2613,6 +2615,7 @@ static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw, struct ieee80211_sta *sta) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); /* @@ -2627,6 +2630,12 @@ static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw, if (sta == rcu_access_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id])) rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id], ERR_PTR(-ENOENT)); + + if (mvm_sta->vif->type == NL80211_IFTYPE_AP) { + mvmvif->ap_assoc_sta_count--; + iwl_mvm_mac_ctxt_cmd_ap(mvm, vif, FW_CTXT_ACTION_MODIFY); + } + mutex_unlock(&mvm->mutex); } diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 72cd67e..ed9b0cc 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -338,6 +338,8 @@ struct iwl_mvm_vif_bf_data { * @bssid: BSSID for this (client) interface * @associated: indicates that we're currently associated, used only for * managing the firmware state in iwl_mvm_bss_info_changed_station() + * @ap_assoc_sta_count: count of stations associated to us - valid only + * if VIF type is AP * @uploaded: indicates the MAC context has been added to the device * @ap_ibss_active: indicates that AP/IBSS is configured and that the interface * should get quota etc. @@ -367,6 +369,7 @@ struct iwl_mvm_vif { u8 bssid[ETH_ALEN]; bool associated; + u8 ap_assoc_sta_count; bool uploaded; bool ap_ibss_active; @@ -1131,6 +1134,9 @@ void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm, struct ieee80211_vif *vif); unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm, struct ieee80211_vif *exclude_vif); +int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 action); /* Bindings */ int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index df216cd..fe2f538 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -275,6 +275,11 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, if (sta_id == IWL_MVM_STATION_COUNT) return -ENOSPC; + if (vif->type == NL80211_IFTYPE_AP) { + mvmvif->ap_assoc_sta_count++; + iwl_mvm_mac_ctxt_cmd_ap(mvm, vif, FW_CTXT_ACTION_MODIFY); + } + spin_lock_init(&mvm_sta->lock); mvm_sta->sta_id = sta_id; -- cgit v0.10.2 From cf8c3cff3fcce966c6d31001c09c92778f961eea Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Mon, 24 Aug 2015 14:53:25 +0300 Subject: iwlwifi: mvm: make sure d0i3 exit work runs before suspending If we are in d0i3 when entering suspend, we leave d0i3 so that mac80211 can call us to remove connections or whatever before going to suspend. We do this by calling pm_runtime_resume() early in the slave transport flow and reactivating it later, when the wiphy suspend flow runs. The problem is that we queue a work in order to leave d0i3. If this work hasn't run yet when the wiphy suspend flow is called, we have a race and entering d0i3 fails (because we're still holding the IWL_MVM_REF_EXIT_WORK reference). To solve this, simply flush the d0i3_exit_work at the beginning of the iwl_mvm_suspend() function. Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index 04264e4..1d54355 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c @@ -1170,6 +1170,9 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); int ret; + /* make sure the d0i3 exit work is not pending */ + flush_work(&mvm->d0i3_exit_work); + ret = iwl_trans_suspend(mvm->trans); if (ret) return ret; -- cgit v0.10.2 From cfe417bc47ba8f470941a1b8ad60911e163f1b34 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 26 Aug 2015 12:22:12 +0200 Subject: iwlwifi: mvm: don't NUL-terminate low-latency debugfs file There's obviously no reason to write a NUL-terminating byte into the debugfs file. Apparently nobody tried to use this from a tool that doesn't strip NUL bytes though, so we never noticed. Fix it. Signed-off-by: Johannes Berg Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c index 383a316..334ae56 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c @@ -1250,11 +1250,10 @@ static ssize_t iwl_dbgfs_low_latency_read(struct file *file, { struct ieee80211_vif *vif = file->private_data; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - char buf[3]; + char buf[2]; buf[0] = mvmvif->low_latency ? '1' : '0'; buf[1] = '\n'; - buf[2] = '\0'; return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf)); } -- cgit v0.10.2 From 3c1f84a174e425f2e7a687a6cfd791f609cbc9eb Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 26 Aug 2015 13:48:55 +0300 Subject: iwlwifi: mvm: don't load -12.ucode anymore -13.ucode has been published long ago. Deprecate old versions of the firmware. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c index 6951aba..22c2430 100644 --- a/drivers/net/wireless/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/iwlwifi/iwl-7000.c @@ -72,11 +72,11 @@ #define IWL7260_UCODE_API_MAX 17 /* Oldest version we won't warn about */ -#define IWL7260_UCODE_API_OK 12 +#define IWL7260_UCODE_API_OK 13 #define IWL3165_UCODE_API_OK 13 /* Lowest firmware API version supported */ -#define IWL7260_UCODE_API_MIN 12 +#define IWL7260_UCODE_API_MIN 13 #define IWL3165_UCODE_API_MIN 13 /* NVM versions */ diff --git a/drivers/net/wireless/iwlwifi/iwl-8000.c b/drivers/net/wireless/iwlwifi/iwl-8000.c index 197abe4..6efef46 100644 --- a/drivers/net/wireless/iwlwifi/iwl-8000.c +++ b/drivers/net/wireless/iwlwifi/iwl-8000.c @@ -72,10 +72,10 @@ #define IWL8000_UCODE_API_MAX 17 /* Oldest version we won't warn about */ -#define IWL8000_UCODE_API_OK 12 +#define IWL8000_UCODE_API_OK 13 /* Lowest firmware API version supported */ -#define IWL8000_UCODE_API_MIN 12 +#define IWL8000_UCODE_API_MIN 13 /* NVM versions */ #define IWL8000_NVM_VERSION 0x0a1d -- cgit v0.10.2 From 79f682806240741f62faaa33d933ebe768caa07f Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Mon, 24 Aug 2015 16:23:58 +0300 Subject: iwlwifi: mvm: remove SCD_QUEUE_CONFIG TLV flag We don't support firmwares that don't use the new API. This also allows to use all the SCD queues, so increase the reported number of queues to 31. Signed-off-by: Liad Kaufman Signed-off-by: Emmanuel Grumbach Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c index 22c2430..5b25f36 100644 --- a/drivers/net/wireless/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/iwlwifi/iwl-7000.c @@ -113,7 +113,7 @@ static const struct iwl_base_params iwl7000_base_params = { .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_7000, - .num_of_queues = IWLAGN_NUM_QUEUES, + .num_of_queues = 31, .pll_cfg_val = 0, .shadow_ram_support = true, .led_compensation = 57, diff --git a/drivers/net/wireless/iwlwifi/iwl-8000.c b/drivers/net/wireless/iwlwifi/iwl-8000.c index 6efef46..0116e5a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-8000.c +++ b/drivers/net/wireless/iwlwifi/iwl-8000.c @@ -107,7 +107,7 @@ static const struct iwl_base_params iwl8000_base_params = { .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_8000, - .num_of_queues = IWLAGN_NUM_QUEUES, + .num_of_queues = 31, .pll_cfg_val = 0, .shadow_ram_support = true, .led_compensation = 57, diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h index 84653e3..483b1274 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h @@ -250,8 +250,6 @@ typedef unsigned int __bitwise__ iwl_ucode_tlv_api_t; * IWL_UCODE_TLV_API_HDC_PHASE_0: ucode supports finer configuration of LTR * @IWL_UCODE_TLV_API_TX_POWER_DEV: new API for tx power. * @IWL_UCODE_TLV_API_WIDE_CMD_HDR: ucode supports wide command header - * @IWL_UCODE_TLV_API_SCD_CFG: This firmware can configure the scheduler - * through the dedicated host command. * @IWL_UCODE_TLV_API_SINGLE_SCAN_EBS: EBS is supported for single scans too. * @IWL_UCODE_TLV_API_ASYNC_DTM: Async temperature notifications are supported. * @IWL_UCODE_TLV_API_LQ_SS_PARAMS: Configure STBC/BFER via LQ CMD ss_params @@ -269,7 +267,6 @@ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_HDC_PHASE_0 = (__force iwl_ucode_tlv_api_t)10, IWL_UCODE_TLV_API_TX_POWER_DEV = (__force iwl_ucode_tlv_api_t)11, IWL_UCODE_TLV_API_WIDE_CMD_HDR = (__force iwl_ucode_tlv_api_t)14, - IWL_UCODE_TLV_API_SCD_CFG = (__force iwl_ucode_tlv_api_t)15, IWL_UCODE_TLV_API_SINGLE_SCAN_EBS = (__force iwl_ucode_tlv_api_t)16, IWL_UCODE_TLV_API_ASYNC_DTM = (__force iwl_ucode_tlv_api_t)17, IWL_UCODE_TLV_API_LQ_SS_PARAMS = (__force iwl_ucode_tlv_api_t)18, diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index ed9b0cc..cf1f514 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -937,11 +937,6 @@ static inline bool iwl_mvm_is_wifi_mcc_supported(struct iwl_mvm *mvm) IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC); } -static inline bool iwl_mvm_is_scd_cfg_supported(struct iwl_mvm *mvm) -{ - return fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_SCD_CFG); -} - static inline bool iwl_mvm_bt_is_plcr_supported(struct iwl_mvm *mvm) { return fw_has_capa(&mvm->fw->ucode_capa, diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index a7d4342..06cba97 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -672,12 +672,6 @@ void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, u16 ssn, .tid = cfg->tid, }; - if (!iwl_mvm_is_scd_cfg_supported(mvm)) { - iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn, cfg, - wdg_timeout); - return; - } - iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn, NULL, wdg_timeout); WARN(iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, sizeof(cmd), &cmd), "Failed to configure queue %d on FIFO %d\n", queue, cfg->fifo); @@ -691,11 +685,6 @@ void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, u8 flags) }; int ret; - if (!iwl_mvm_is_scd_cfg_supported(mvm)) { - iwl_trans_txq_disable(mvm->trans, queue, true); - return; - } - iwl_trans_txq_disable(mvm->trans, queue, false); ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, flags, sizeof(cmd), &cmd); -- cgit v0.10.2 From 6986fdd699f9be57cc1478c738ea6347e3d52547 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Thu, 27 Aug 2015 14:47:33 +0200 Subject: ath10k: fix mu-mimo rx status reporting MU-MIMO Rx involves different interpretation of the VHT-SIG-A compared to SU-MIMO. The incorrect interpretation led ath10k to report VHT MCS values greater than 9 which subsequently prompted mac80211 to drop such frames. This effectively broke Rx with MU-MIMO in many cases and manifested with a kernel warning in the log which looked like this: [ 14.552520] WARNING: CPU: 2 PID: 0 at net/mac80211/rx.c:3578 ieee80211_rx+0x26c/0x940 [mac80211]() [ 14.552522] Rate marked as a VHT rate but data is invalid: MCS: 10, NSS: 2 ... call trace follows ... Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 1b7a043..8ba8fa90 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -643,6 +643,8 @@ struct amsdu_subframe_hdr { __be16 len; } __packed; +#define GROUP_ID_IS_SU_MIMO(x) ((x) == 0 || (x) == 63) + static void ath10k_htt_rx_h_rates(struct ath10k *ar, struct ieee80211_rx_status *status, struct htt_rx_desc *rxd) @@ -650,6 +652,7 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar, struct ieee80211_supported_band *sband; u8 cck, rate, bw, sgi, mcs, nss; u8 preamble = 0; + u8 group_id; u32 info1, info2, info3; info1 = __le32_to_cpu(rxd->ppdu_start.info1); @@ -692,10 +695,27 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar, case HTT_RX_VHT_WITH_TXBF: /* VHT-SIG-A1 in info2, VHT-SIG-A2 in info3 TODO check this */ - mcs = (info3 >> 4) & 0x0F; - nss = ((info2 >> 10) & 0x07) + 1; bw = info2 & 3; sgi = info3 & 1; + group_id = (info2 >> 4) & 0x3F; + + if (GROUP_ID_IS_SU_MIMO(group_id)) { + mcs = (info3 >> 4) & 0x0F; + nss = ((info2 >> 10) & 0x07) + 1; + } else { + /* Hardware doesn't decode VHT-SIG-B into Rx descriptor + * so it's impossible to decode MCS. Also since + * firmware consumes Group Id Management frames host + * has no knowledge regarding group/user position + * mapping so it's impossible to pick the correct Nsts + * from VHT-SIG-A1. + * + * Bandwidth and SGI are valid so report the rateinfo + * on best-effort basis. + */ + mcs = 0; + nss = 1; + } status->rate_idx = mcs; status->vht_nss = nss; -- cgit v0.10.2 From a925a3763982799460b9e9b5299520172652b785 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Fri, 28 Aug 2015 17:21:34 +0530 Subject: ath10k: fix DMA alloc failure for target requested memory chunks During long hours of stress testing like AP interface up/down along with continuous ping flood from a station doing connect/disconnect, it is observed that the system is not able to allocate DMA consistent memory of size > 512KB chunks as requested by firmware in WMI_SERVICE_EVENTID. With the system memory getting fragmented during the run based on the size of the memory requested, the failure to return physically continguous memory of high order can happen. Once the system gets to this situation, bringing up the wifi interface will fail and a system reboot may be needed to make it work again. This problem is obseved with QCA99X0. To fix this issue, allocate the DMA memory requested by firmware during device probe time and keep it during the life time of the device. WMI service ready event handler is changed to allocate the memory chunks if it is not already allocated or if the memory allocated for the previous ready event is not same as the current requested ones. After this patch the memory usage when wifi is inactive will be inceased by few 100KB to 3MB based on the target type. Failure happens with the following stack trace [29557.488773] kworker/u4:1: page allocation failure: order:8, mode:0xd0 [29557.494297] CPU: 0 PID: 8402 Comm: kworker/u4:1 Not tainted 3.14.43 #7 [29557.500793] Workqueue: ath10k_aux_wq ath10k_wmi_event_service_ready_work [ath10k_core] [29557.508602] [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [29557.516580] [] (show_stack) from [] (dump_stack+0x88/0xcc) [29557.523612] [] (dump_stack) from [] (warn_alloc_failed+0xdc/0x108) [29557.531515] [] (warn_alloc_failed) from [] (__alloc_pages_nodemask+0x4f0/0x654) [29557.540485] [] (__alloc_pages_nodemask) from [] (__dma_alloc_buffer.isra.20+0x2c/0x104) [29557.550260] [] (__dma_alloc_buffer.isra.20) from [] (__alloc_remap_buffer.isra.23+0x14/0xb8) [29557.560413] [] (__alloc_remap_buffer.isra.23) from [] (__dma_alloc+0x224/0x2b8) [29557.569490] [] (__dma_alloc) from [] (arm_dma_alloc+0x84/0x90) [29557.577010] [] (arm_dma_alloc) from [] (ath10k_wmi_event_service_ready_work+0x2f8/0x420 [ath10k_core]) [29557.588055] [] (ath10k_wmi_event_service_ready_work [ath10k_core]) from [] (process_one_work+0x20c/0x328) [29557.599305] [] (process_one_work) from [] (worker_thread+0x228/0x360) [29557.607470] [] (worker_thread) from [] (kthread+0xd8/0xec) [29557.614750] [] (kthread) from [] (ret_from_fork+0x14/0x3c) [29557.712751] Normal: 696*4kB (UEMR) 512*8kB (UEMR) 367*16kB (UEMR) 404*32kB (UEMR) 455*64kB (UEMR) 424*128kB (UEMR) 379*256kB (UMR) 327*512kB (UMR) 1*1024kB (R) 0*2048kB 0*4096kB = 374544kB Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index b87b986..6e5aeed 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -1714,6 +1714,7 @@ void ath10k_core_destroy(struct ath10k *ar) destroy_workqueue(ar->workqueue_aux); ath10k_debug_destroy(ar); + ath10k_wmi_free_host_mem(ar); ath10k_mac_destroy(ar); } EXPORT_SYMBOL(ath10k_core_destroy); diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index ce01107..87d9de2 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -3917,6 +3917,53 @@ static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id, return 0; } +static bool +ath10k_wmi_is_host_mem_allocated(struct ath10k *ar, + const struct wlan_host_mem_req **mem_reqs, + u32 num_mem_reqs) +{ + u32 req_id, num_units, unit_size, num_unit_info; + u32 pool_size; + int i, j; + bool found; + + if (ar->wmi.num_mem_chunks != num_mem_reqs) + return false; + + for (i = 0; i < num_mem_reqs; ++i) { + req_id = __le32_to_cpu(mem_reqs[i]->req_id); + num_units = __le32_to_cpu(mem_reqs[i]->num_units); + unit_size = __le32_to_cpu(mem_reqs[i]->unit_size); + num_unit_info = __le32_to_cpu(mem_reqs[i]->num_unit_info); + + if (num_unit_info & NUM_UNITS_IS_NUM_ACTIVE_PEERS) { + if (ar->num_active_peers) + num_units = ar->num_active_peers + 1; + else + num_units = ar->max_num_peers + 1; + } else if (num_unit_info & NUM_UNITS_IS_NUM_PEERS) { + num_units = ar->max_num_peers + 1; + } else if (num_unit_info & NUM_UNITS_IS_NUM_VDEVS) { + num_units = ar->max_num_vdevs + 1; + } + + found = false; + for (j = 0; j < ar->wmi.num_mem_chunks; j++) { + if (ar->wmi.mem_chunks[j].req_id == req_id) { + pool_size = num_units * round_up(unit_size, 4); + if (ar->wmi.mem_chunks[j].len == pool_size) { + found = true; + break; + } + } + } + if (!found) + return false; + } + + return true; +} + static int ath10k_wmi_main_op_pull_svc_rdy_ev(struct ath10k *ar, struct sk_buff *skb, struct wmi_svc_rdy_ev_arg *arg) @@ -3997,6 +4044,7 @@ static void ath10k_wmi_event_service_ready_work(struct work_struct *work) struct wmi_svc_rdy_ev_arg arg = {}; u32 num_units, req_id, unit_size, num_mem_reqs, num_unit_info, i; int ret; + bool allocated; if (!skb) { ath10k_warn(ar, "invalid service ready event skb\n"); @@ -4073,6 +4121,18 @@ static void ath10k_wmi_event_service_ready_work(struct work_struct *work) * and WMI_SERVICE_IRAM_TIDS, etc. */ + allocated = ath10k_wmi_is_host_mem_allocated(ar, arg.mem_reqs, + num_mem_reqs); + if (allocated) + goto skip_mem_alloc; + + /* Either this event is received during boot time or there is a change + * in memory requirement from firmware when compared to last request. + * Free any old memory and do a fresh allocation based on the current + * memory requirement. + */ + ath10k_wmi_free_host_mem(ar); + for (i = 0; i < num_mem_reqs; ++i) { req_id = __le32_to_cpu(arg.mem_reqs[i]->req_id); num_units = __le32_to_cpu(arg.mem_reqs[i]->num_units); @@ -4108,6 +4168,7 @@ static void ath10k_wmi_event_service_ready_work(struct work_struct *work) return; } +skip_mem_alloc: ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi event service ready min_tx_power 0x%08x max_tx_power 0x%08x ht_cap 0x%08x vht_cap 0x%08x sw_ver0 0x%08x sw_ver1 0x%08x fw_build 0x%08x phy_capab 0x%08x num_rf_chains 0x%08x eeprom_rd 0x%08x num_mem_reqs 0x%08x\n", __le32_to_cpu(arg.min_tx_power), @@ -6660,15 +6721,10 @@ int ath10k_wmi_attach(struct ath10k *ar) return 0; } -void ath10k_wmi_detach(struct ath10k *ar) +void ath10k_wmi_free_host_mem(struct ath10k *ar) { int i; - cancel_work_sync(&ar->svc_rdy_work); - - if (ar->svc_rdy_skb) - dev_kfree_skb(ar->svc_rdy_skb); - /* free the host memory chunks requested by firmware */ for (i = 0; i < ar->wmi.num_mem_chunks; i++) { dma_free_coherent(ar->dev, @@ -6679,3 +6735,11 @@ void ath10k_wmi_detach(struct ath10k *ar) ar->wmi.num_mem_chunks = 0; } + +void ath10k_wmi_detach(struct ath10k *ar) +{ + cancel_work_sync(&ar->svc_rdy_work); + + if (ar->svc_rdy_skb) + dev_kfree_skb(ar->svc_rdy_skb); +} diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 52d3503..3e5a159 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -6067,6 +6067,7 @@ struct ath10k_fw_stats_peer; int ath10k_wmi_attach(struct ath10k *ar); void ath10k_wmi_detach(struct ath10k *ar); +void ath10k_wmi_free_host_mem(struct ath10k *ar); int ath10k_wmi_wait_for_service_ready(struct ath10k *ar); int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar); -- cgit v0.10.2 From 7b7da0a02192fd518c26c46dad6b14aca4569605 Mon Sep 17 00:00:00 2001 From: Vivek Natarajan Date: Mon, 31 Aug 2015 16:34:55 +0530 Subject: ath10k: drop probe responses when too many are queued In a noisy environment, when multiple interfaces are created, the management tx descriptors are fully occupied by the probe responses from all the interfaces. This prevents a new station from a successful association. Fix this by limiting the probe responses when the specified threshold limit is reached. Signed-off-by: Vivek Natarajan Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 6e5aeed..6e5033b 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -54,6 +54,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .has_shifted_cc_wraparound = true, .otp_exe_param = 0, .channel_counters_freq_hz = 88000, + .max_probe_resp_desc_thres = 0, .fw = { .dir = QCA988X_HW_2_0_FW_DIR, .fw = QCA988X_HW_2_0_FW_FILE, @@ -70,6 +71,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .uart_pin = 6, .otp_exe_param = 0, .channel_counters_freq_hz = 88000, + .max_probe_resp_desc_thres = 0, .fw = { .dir = QCA6174_HW_2_1_FW_DIR, .fw = QCA6174_HW_2_1_FW_FILE, @@ -86,6 +88,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .uart_pin = 6, .otp_exe_param = 0, .channel_counters_freq_hz = 88000, + .max_probe_resp_desc_thres = 0, .fw = { .dir = QCA6174_HW_3_0_FW_DIR, .fw = QCA6174_HW_3_0_FW_FILE, @@ -102,6 +105,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .uart_pin = 6, .otp_exe_param = 0, .channel_counters_freq_hz = 88000, + .max_probe_resp_desc_thres = 0, .fw = { /* uses same binaries as hw3.0 */ .dir = QCA6174_HW_3_0_FW_DIR, @@ -120,6 +124,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .otp_exe_param = 0x00000700, .continuous_frag_desc = true, .channel_counters_freq_hz = 150000, + .max_probe_resp_desc_thres = 24, .fw = { .dir = QCA99X0_HW_2_0_FW_DIR, .fw = QCA99X0_HW_2_0_FW_FILE, diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 1254214..04e040a 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -612,6 +612,11 @@ struct ath10k { u32 channel_counters_freq_hz; + /* Mgmt tx descriptors threshold for limiting probe response + * frames. + */ + u32 max_probe_resp_desc_thres; + struct ath10k_hw_params_fw { const char *dir; const char *fw; diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 5731875..5a8e4ea 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -1485,6 +1485,7 @@ struct ath10k_htt { spinlock_t tx_lock; int max_num_pending_tx; int num_pending_tx; + int num_pending_mgmt_tx; struct idr pending_tx; wait_queue_head_t empty_tx_wq; struct dma_pool *tx_pool; @@ -1587,7 +1588,7 @@ int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt, u8 max_subfrms_ampdu, u8 max_subfrms_amsdu); -void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt); +void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt, bool limit_mgmt_desc); int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt, struct sk_buff *skb); void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id); int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *); diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 43aa5e2..ea53c21 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -22,22 +22,28 @@ #include "txrx.h" #include "debug.h" -void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt) +void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt, bool limit_mgmt_desc) { + if (limit_mgmt_desc) + htt->num_pending_mgmt_tx--; + htt->num_pending_tx--; if (htt->num_pending_tx == htt->max_num_pending_tx - 1) ath10k_mac_tx_unlock(htt->ar, ATH10K_TX_PAUSE_Q_FULL); } -static void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt) +static void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt, + bool limit_mgmt_desc) { spin_lock_bh(&htt->tx_lock); - __ath10k_htt_tx_dec_pending(htt); + __ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc); spin_unlock_bh(&htt->tx_lock); } -static int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt) +static int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt, + bool limit_mgmt_desc, bool is_probe_resp) { + struct ath10k *ar = htt->ar; int ret = 0; spin_lock_bh(&htt->tx_lock); @@ -47,6 +53,15 @@ static int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt) goto exit; } + if (limit_mgmt_desc) { + if (is_probe_resp && (htt->num_pending_mgmt_tx > + ar->hw_params.max_probe_resp_desc_thres)) { + ret = -EBUSY; + goto exit; + } + htt->num_pending_mgmt_tx++; + } + htt->num_pending_tx++; if (htt->num_pending_tx == htt->max_num_pending_tx) ath10k_mac_tx_lock(htt->ar, ATH10K_TX_PAUSE_Q_FULL); @@ -417,8 +432,19 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) int len = 0; int msdu_id = -1; int res; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data; + bool limit_mgmt_desc = false; + bool is_probe_resp = false; + + if (ar->hw_params.max_probe_resp_desc_thres) { + limit_mgmt_desc = true; + + if (ieee80211_is_probe_resp(hdr->frame_control)) + is_probe_resp = true; + } + + res = ath10k_htt_tx_inc_pending(htt, limit_mgmt_desc, is_probe_resp); - res = ath10k_htt_tx_inc_pending(htt); if (res) goto err; @@ -476,7 +502,7 @@ err_free_msdu_id: ath10k_htt_tx_free_msdu_id(htt, msdu_id); spin_unlock_bh(&htt->tx_lock); err_tx_dec: - ath10k_htt_tx_dec_pending(htt); + ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc); err: return res; } @@ -498,8 +524,18 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) dma_addr_t paddr = 0; u32 frags_paddr = 0; struct htt_msdu_ext_desc *ext_desc = NULL; + bool limit_mgmt_desc = false; + bool is_probe_resp = false; + + if (unlikely(ieee80211_is_mgmt(hdr->frame_control)) && + ar->hw_params.max_probe_resp_desc_thres) { + limit_mgmt_desc = true; + + if (ieee80211_is_probe_resp(hdr->frame_control)) + is_probe_resp = true; + } - res = ath10k_htt_tx_inc_pending(htt); + res = ath10k_htt_tx_inc_pending(htt, limit_mgmt_desc, is_probe_resp); if (res) goto err; @@ -678,7 +714,7 @@ err_free_msdu_id: ath10k_htt_tx_free_msdu_id(htt, msdu_id); spin_unlock_bh(&htt->tx_lock); err_tx_dec: - ath10k_htt_tx_dec_pending(htt); + ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc); err: return res; } diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c index e4a9c4c..7db7d50 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.c +++ b/drivers/net/wireless/ath/ath10k/txrx.c @@ -52,6 +52,9 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct ieee80211_tx_info *info; struct ath10k_skb_cb *skb_cb; struct sk_buff *msdu; + struct ieee80211_hdr *hdr; + __le16 fc; + bool limit_mgmt_desc = false; ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx completion msdu_id %u discard %d no_ack %d success %d\n", @@ -72,14 +75,21 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt, spin_unlock_bh(&htt->tx_lock); return; } + + hdr = (struct ieee80211_hdr *)msdu->data; + fc = hdr->frame_control; + + if (unlikely(ieee80211_is_mgmt(fc)) && + ar->hw_params.max_probe_resp_desc_thres) + limit_mgmt_desc = true; + ath10k_htt_tx_free_msdu_id(htt, tx_done->msdu_id); - __ath10k_htt_tx_dec_pending(htt); + __ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc); if (htt->num_pending_tx == 0) wake_up(&htt->empty_tx_wq); spin_unlock_bh(&htt->tx_lock); skb_cb = ATH10K_SKB_CB(msdu); - dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); if (skb_cb->htt.txbuf) -- cgit v0.10.2 From 707a0c811ca02861f1500f0696efebcf3bd2e2e2 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 2 Sep 2015 13:20:19 +0200 Subject: ath10k: fix beamformee VHT STS capability The VHT STS CAP shall be reported by firmware to host, like in case of QCA99x0. For QCA6174 hw family this isn't set for some reason. So for this particular chips, let's assume it has the ability to support VHT NDP in up to 4 STSs (which is true by the way). Change the published beamformee STS cap accordingly to 3 or to what the firmware reports. Assumption so far, it suppose to be the num_rf_chains-1, was completely wrong. Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 64674c9..3ecc448 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -4064,17 +4064,35 @@ static u32 get_nss_from_chainmask(u16 chain_mask) return 1; } +static int ath10k_mac_get_vht_cap_bf_sts(struct ath10k *ar) +{ + int nsts = ar->vht_cap_info; + nsts &= IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; + nsts >>= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT; + + /* If firmware does not deliver to host number of space-time + * streams supported, assume it support up to 4 BF STS and return + * the value for VHT CAP: nsts-1) + * */ + if (nsts == 0) + return 3; + + return nsts; +} + static int ath10k_mac_set_txbf_conf(struct ath10k_vif *arvif) { u32 value = 0; struct ath10k *ar = arvif->ar; + int nsts; if (ath10k_wmi_get_txbf_conf_scheme(ar) != WMI_TXBF_CONF_BEFORE_ASSOC) return 0; + nsts = ath10k_mac_get_vht_cap_bf_sts(ar); if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)) - value |= SM((ar->num_rf_chains - 1), WMI_TXBF_STS_CAP_OFFSET); + value |= SM(nsts, WMI_TXBF_STS_CAP_OFFSET); if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)) @@ -6804,7 +6822,7 @@ static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar) if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)) { - val = ar->num_rf_chains - 1; + val = ath10k_mac_get_vht_cap_bf_sts(ar); val <<= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT; val &= IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; -- cgit v0.10.2 From 0c6d6f2606dfe3758265fb29bfe8103e89d99bab Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 2 Sep 2015 13:20:20 +0200 Subject: ath10k: fix beamformer VHT sounding dimensions capability Similarly to the VHT STS, this is supposed to be propagated by firmware. In case it's not, use the default value, but as last resort. Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 3ecc448..989d2a1 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -4080,11 +4080,27 @@ static int ath10k_mac_get_vht_cap_bf_sts(struct ath10k *ar) return nsts; } +static int ath10k_mac_get_vht_cap_bf_sound_dim(struct ath10k *ar) +{ + int sound_dim = ar->vht_cap_info; + sound_dim &= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK; + sound_dim >>=IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT; + + /* If the sounding dimension is not advertised by the firmware, + * let's use a default value of 1 + */ + if (sound_dim == 0) + return 1; + + return sound_dim; +} + static int ath10k_mac_set_txbf_conf(struct ath10k_vif *arvif) { u32 value = 0; struct ath10k *ar = arvif->ar; int nsts; + int sound_dim; if (ath10k_wmi_get_txbf_conf_scheme(ar) != WMI_TXBF_CONF_BEFORE_ASSOC) return 0; @@ -4094,9 +4110,10 @@ static int ath10k_mac_set_txbf_conf(struct ath10k_vif *arvif) IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)) value |= SM(nsts, WMI_TXBF_STS_CAP_OFFSET); + sound_dim = ath10k_mac_get_vht_cap_bf_sound_dim(ar); if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)) - value |= SM((ar->num_rf_chains - 1), WMI_BF_SOUND_DIM_OFFSET); + value |= SM(sound_dim, WMI_BF_SOUND_DIM_OFFSET); if (!value) return 0; @@ -6831,7 +6848,7 @@ static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar) if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)) { - val = ar->num_rf_chains - 1; + val = ath10k_mac_get_vht_cap_bf_sound_dim(ar); val <<= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT; val &= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK; -- cgit v0.10.2 From 6ccea107eba46fbc7de40e944bf17145c2809666 Mon Sep 17 00:00:00 2001 From: Manikanta Pubbisetty Date: Wed, 2 Sep 2015 17:05:27 +0300 Subject: ath10k: print invalid mcs reported in rx descriptor Sometimes hardware reports invalid mcs index in rx descriptor when operating in VHT80 mode and all packets with invalid mcs will be eventually dropped in mac80211. This issue is observerd during testing on QCA99X0 chipsets. This patch adds a warn message for dumping the rx desc info which helps in analysing the issue when invalid mcs is received. Signed-off-by: Manikanta Pubbisetty Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 8ba8fa90..606c1a3 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -717,6 +717,29 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar, nss = 1; } + if (mcs > 0x09) { + ath10k_warn(ar, "invalid MCS received %u\n", mcs); + ath10k_warn(ar, "rxd %08x mpdu start %08x %08x msdu start %08x %08x ppdu start %08x %08x %08x %08x %08x\n", + __le32_to_cpu(rxd->attention.flags), + __le32_to_cpu(rxd->mpdu_start.info0), + __le32_to_cpu(rxd->mpdu_start.info1), + __le32_to_cpu(rxd->msdu_start.common.info0), + __le32_to_cpu(rxd->msdu_start.common.info1), + rxd->ppdu_start.info0, + __le32_to_cpu(rxd->ppdu_start.info1), + __le32_to_cpu(rxd->ppdu_start.info2), + __le32_to_cpu(rxd->ppdu_start.info3), + __le32_to_cpu(rxd->ppdu_start.info4)); + + ath10k_warn(ar, "msdu end %08x mpdu end %08x\n", + __le32_to_cpu(rxd->msdu_end.common.info0), + __le32_to_cpu(rxd->mpdu_end.info0)); + + ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, + "rx desc msdu payload: ", + rxd->msdu_payload, 50); + } + status->rate_idx = mcs; status->vht_nss = nss; -- cgit v0.10.2 From 0d031c8995bcc065f9c5778069f2131f7d92d4d5 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Wed, 9 Sep 2015 12:47:34 -0400 Subject: ath10k: enable monitor when OTHER_BSS requested By default, ath10k restricts received frames to those matching BSSID. When other BSS frames are requested (e.g. in mesh mode), add an internal monitor device so those frames are not filtered. Signed-off-by: Bob Copeland Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 989d2a1..80efcc8 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1070,6 +1070,7 @@ 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_CAC_RUNNING, &ar->dev_flags); } -- cgit v0.10.2 From bc76c28719d4333248a516fd4d469ab741b44b63 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Wed, 9 Sep 2015 12:47:35 -0400 Subject: ath10k: check for encryption before adding MIC_LEN In the case of raw mode without nohwcrypt parameter, we should still make sure the frame is protected before adding MIC_LEN to avoid skb_under_panic errors. Signed-off-by: Bob Copeland Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index ea53c21..eb5ba9b 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -564,7 +564,8 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) ieee80211_has_protected(hdr->frame_control)) { skb_put(msdu, IEEE80211_CCMP_MIC_LEN); } else if (!skb_cb->htt.nohwcrypt && - skb_cb->txmode == ATH10K_HW_TXRX_RAW) { + skb_cb->txmode == ATH10K_HW_TXRX_RAW && + ieee80211_has_protected(hdr->frame_control)) { skb_put(msdu, IEEE80211_CCMP_MIC_LEN); } -- cgit v0.10.2 From b6c7bafa7d4b1398cce93e4af0a48603919fa933 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Wed, 9 Sep 2015 12:47:36 -0400 Subject: ath10k: implement mesh support Add support for mesh to ath10k. We simply use an AP virtual interface in the firmware in order to enable beaconing without TSF adoption, and use the raw (802.11) transmit mode. Due to firmware limitations, the firmware must operate in raw (non-native 802.11) mode. As this is configured at firmware init time, a new "rawmode" modparam is added, and mesh interfaces are available only if rawmode=true. The firmware must advertise support for rawmode; tested successfully with firmware 10.2.4.70.6-2. When the module is loaded with (newly implemented) modparam rawmode=1, it will enable operating an open mesh STA via something like the following: ip link set wlan0 down iw dev wlan0 set type mp ip link set wlan0 up iw dev wlan0 set freq 5745 80 5775 iw dev wlan0 mesh join mesh-vht Signed-off-by: Bob Copeland Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 6e5033b..09a74a8 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -34,16 +34,19 @@ unsigned int ath10k_debug_mask; static unsigned int ath10k_cryptmode_param; static bool uart_print; static bool skip_otp; +static bool rawmode; module_param_named(debug_mask, ath10k_debug_mask, uint, 0644); module_param_named(cryptmode, ath10k_cryptmode_param, uint, 0644); module_param(uart_print, bool, 0644); module_param(skip_otp, bool, 0644); +module_param(rawmode, bool, 0644); MODULE_PARM_DESC(debug_mask, "Debugging mask"); MODULE_PARM_DESC(uart_print, "Uart target debugging"); MODULE_PARM_DESC(skip_otp, "Skip otp failure for calibration in testmode"); MODULE_PARM_DESC(cryptmode, "Crypto mode: 0-hardware, 1-software"); +MODULE_PARM_DESC(rawmode, "Use raw 802.11 frame datapath"); static const struct ath10k_hw_params ath10k_hw_params_list[] = { { @@ -1122,6 +1125,15 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar) ar->htt.max_num_amsdu = ATH10K_HTT_MAX_NUM_AMSDU_DEFAULT; ar->htt.max_num_ampdu = ATH10K_HTT_MAX_NUM_AMPDU_DEFAULT; + if (rawmode) { + if (!test_bit(ATH10K_FW_FEATURE_RAW_MODE_SUPPORT, + ar->fw_features)) { + ath10k_err(ar, "rawmode = 1 requires support from firmware"); + return -EINVAL; + } + set_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags); + } + if (test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) { ar->wmi.rx_decap_mode = ATH10K_HW_TXRX_RAW; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 80efcc8..1cda4f9 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -4211,6 +4211,14 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, case NL80211_IFTYPE_ADHOC: arvif->vdev_type = WMI_VDEV_TYPE_IBSS; break; + case NL80211_IFTYPE_MESH_POINT: + if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) { + ret = -EINVAL; + ath10k_warn(ar, "must load driver with rawmode=1 to add mesh interfaces\n"); + goto err; + } + arvif->vdev_type = WMI_VDEV_TYPE_AP; + break; case NL80211_IFTYPE_AP: arvif->vdev_type = WMI_VDEV_TYPE_AP; @@ -4251,6 +4259,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, * become corrupted, e.g. have garbled IEs or out-of-date TIM bitmap. */ if (vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_MESH_POINT || vif->type == NL80211_IFTYPE_AP) { arvif->beacon_buf = dma_zalloc_coherent(ar->dev, IEEE80211_MAX_FRAME_LEN, @@ -4590,6 +4599,13 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, if (ret) ath10k_warn(ar, "failed to update beacon template: %d\n", ret); + + if (ieee80211_vif_is_mesh(vif)) { + /* mesh doesn't use SSID but firmware needs it */ + strncpy(arvif->u.ap.ssid, "mesh", + sizeof(arvif->u.ap.ssid)); + arvif->u.ap.ssid_len = 4; + } } if (changed & BSS_CHANGED_AP_PROBE_RESP) { @@ -5329,6 +5345,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, } else if (old_state == IEEE80211_STA_AUTH && new_state == IEEE80211_STA_ASSOC && (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_MESH_POINT || vif->type == NL80211_IFTYPE_ADHOC)) { /* * New association. @@ -5364,6 +5381,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTH && (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_MESH_POINT || vif->type == NL80211_IFTYPE_ADHOC)) { /* * Disassociation. @@ -6678,6 +6696,9 @@ static const struct ieee80211_iface_limit ath10k_if_limits[] = { { .max = 7, .types = BIT(NL80211_IFTYPE_AP) +#ifdef CONFIG_MAC80211_MESH + | BIT(NL80211_IFTYPE_MESH_POINT) +#endif }, }; @@ -6685,6 +6706,9 @@ static const struct ieee80211_iface_limit ath10k_10x_if_limits[] = { { .max = 8, .types = BIT(NL80211_IFTYPE_AP) +#ifdef CONFIG_MAC80211_MESH + | BIT(NL80211_IFTYPE_MESH_POINT) +#endif }, }; @@ -6722,6 +6746,9 @@ static const struct ieee80211_iface_limit ath10k_tlv_if_limit[] = { { .max = 2, .types = BIT(NL80211_IFTYPE_AP) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO), }, @@ -6743,6 +6770,9 @@ static const struct ieee80211_iface_limit ath10k_tlv_qcs_if_limit[] = { { .max = 1, .types = BIT(NL80211_IFTYPE_AP) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif BIT(NL80211_IFTYPE_P2P_GO), }, { @@ -6809,6 +6839,9 @@ static const struct ieee80211_iface_limit ath10k_10_4_if_limits[] = { { .max = 16, .types = BIT(NL80211_IFTYPE_AP) +#ifdef CONFIG_MAC80211_MESH + | BIT(NL80211_IFTYPE_MESH_POINT) +#endif }, }; @@ -7033,7 +7066,8 @@ int ath10k_mac_register(struct ath10k *ar) ar->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_AP); + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT); ar->hw->wiphy->available_antennas_rx = ar->supp_rx_chainmask; ar->hw->wiphy->available_antennas_tx = ar->supp_tx_chainmask; -- cgit v0.10.2 From 634349bae0b2a23f71cabbdb770d35fffc1aad8c Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Thu, 3 Sep 2015 10:43:45 +0200 Subject: ath10k: move hw_scan worker queuing The remain_on_channel callback needs different timeout. Calling ieee80211_queue_work() with a shorter delay after calling it with a longer delay will not change the timer. This caused the offchannel timeout worker to not trigger in time and caused the device to stay on channel longer then expected. This could cause some problems and was be easily reproduced with `iw offchannel` command. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 1cda4f9..c7e3165 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3618,9 +3618,6 @@ static int ath10k_start_scan(struct ath10k *ar, } spin_unlock_bh(&ar->data_lock); - /* Add a 200ms margin to account for event/command processing */ - ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout, - msecs_to_jiffies(arg->max_scan_time+200)); return 0; } @@ -4803,6 +4800,11 @@ static int ath10k_hw_scan(struct ieee80211_hw *hw, spin_unlock_bh(&ar->data_lock); } + /* Add a 200ms margin to account for event/command processing */ + ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout, + msecs_to_jiffies(arg.max_scan_time + + 200)); + exit: mutex_unlock(&ar->conf_mutex); return ret; -- cgit v0.10.2 From 7be6d1b7625ab20e1dfde07a0c6dc77784efbb48 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Thu, 3 Sep 2015 10:44:51 +0200 Subject: ath10k: split switch_vif_chanctx guts This is necessary to make vdev restarting logic reusable later. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index c7e3165..eee5aa9 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -6274,6 +6274,94 @@ ath10k_mac_update_rx_channel(struct ath10k *ar, rcu_read_unlock(); } +static void +ath10k_mac_update_vif_chan(struct ath10k *ar, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs) +{ + struct ath10k_vif *arvif; + int ret; + int i; + + lockdep_assert_held(&ar->conf_mutex); + + /* First stop monitor interface. Some FW versions crash if there's a + * lone monitor interface. + */ + if (ar->monitor_started) + ath10k_monitor_stop(ar); + + for (i = 0; i < n_vifs; i++) { + arvif = ath10k_vif_to_arvif(vifs[i].vif); + + ath10k_dbg(ar, ATH10K_DBG_MAC, + "mac chanctx switch vdev_id %i freq %hu->%hu width %d->%d\n", + arvif->vdev_id, + vifs[i].old_ctx->def.chan->center_freq, + vifs[i].new_ctx->def.chan->center_freq, + vifs[i].old_ctx->def.width, + vifs[i].new_ctx->def.width); + + if (WARN_ON(!arvif->is_started)) + continue; + + if (WARN_ON(!arvif->is_up)) + continue; + + ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id); + if (ret) { + ath10k_warn(ar, "failed to down vdev %d: %d\n", + arvif->vdev_id, ret); + continue; + } + } + + /* All relevant vdevs are downed and associated channel resources + * should be available for the channel switch now. + */ + + spin_lock_bh(&ar->data_lock); + ath10k_mac_update_rx_channel(ar, NULL, vifs, n_vifs); + spin_unlock_bh(&ar->data_lock); + + for (i = 0; i < n_vifs; i++) { + arvif = ath10k_vif_to_arvif(vifs[i].vif); + + if (WARN_ON(!arvif->is_started)) + continue; + + if (WARN_ON(!arvif->is_up)) + continue; + + ret = ath10k_mac_setup_bcn_tmpl(arvif); + if (ret) + ath10k_warn(ar, "failed to update bcn tmpl during csa: %d\n", + ret); + + ret = ath10k_mac_setup_prb_tmpl(arvif); + if (ret) + ath10k_warn(ar, "failed to update prb tmpl during csa: %d\n", + ret); + + ret = ath10k_vdev_restart(arvif, &vifs[i].new_ctx->def); + if (ret) { + ath10k_warn(ar, "failed to restart vdev %d: %d\n", + arvif->vdev_id, ret); + continue; + } + + ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid, + arvif->bssid); + if (ret) { + ath10k_warn(ar, "failed to bring vdev up %d: %d\n", + arvif->vdev_id, ret); + continue; + } + } + + ath10k_monitor_recalc(ar); +} + static int ath10k_mac_op_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx) @@ -6458,91 +6546,13 @@ ath10k_mac_op_switch_vif_chanctx(struct ieee80211_hw *hw, enum ieee80211_chanctx_switch_mode mode) { struct ath10k *ar = hw->priv; - struct ath10k_vif *arvif; - int ret; - int i; mutex_lock(&ar->conf_mutex); ath10k_dbg(ar, ATH10K_DBG_MAC, "mac chanctx switch n_vifs %d mode %d\n", n_vifs, mode); - - /* First stop monitor interface. Some FW versions crash if there's a - * lone monitor interface. - */ - if (ar->monitor_started) - ath10k_monitor_stop(ar); - - for (i = 0; i < n_vifs; i++) { - arvif = ath10k_vif_to_arvif(vifs[i].vif); - - ath10k_dbg(ar, ATH10K_DBG_MAC, - "mac chanctx switch vdev_id %i freq %hu->%hu width %d->%d\n", - arvif->vdev_id, - vifs[i].old_ctx->def.chan->center_freq, - vifs[i].new_ctx->def.chan->center_freq, - vifs[i].old_ctx->def.width, - vifs[i].new_ctx->def.width); - - if (WARN_ON(!arvif->is_started)) - continue; - - if (WARN_ON(!arvif->is_up)) - continue; - - ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id); - if (ret) { - ath10k_warn(ar, "failed to down vdev %d: %d\n", - arvif->vdev_id, ret); - continue; - } - } - - /* All relevant vdevs are downed and associated channel resources - * should be available for the channel switch now. - */ - - spin_lock_bh(&ar->data_lock); - ath10k_mac_update_rx_channel(ar, NULL, vifs, n_vifs); - spin_unlock_bh(&ar->data_lock); - - for (i = 0; i < n_vifs; i++) { - arvif = ath10k_vif_to_arvif(vifs[i].vif); - - if (WARN_ON(!arvif->is_started)) - continue; - - if (WARN_ON(!arvif->is_up)) - continue; - - ret = ath10k_mac_setup_bcn_tmpl(arvif); - if (ret) - ath10k_warn(ar, "failed to update bcn tmpl during csa: %d\n", - ret); - - ret = ath10k_mac_setup_prb_tmpl(arvif); - if (ret) - ath10k_warn(ar, "failed to update prb tmpl during csa: %d\n", - ret); - - ret = ath10k_vdev_restart(arvif, &vifs[i].new_ctx->def); - if (ret) { - ath10k_warn(ar, "failed to restart vdev %d: %d\n", - arvif->vdev_id, ret); - continue; - } - - ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid, - arvif->bssid); - if (ret) { - ath10k_warn(ar, "failed to bring vdev up %d: %d\n", - arvif->vdev_id, ret); - continue; - } - } - - ath10k_monitor_recalc(ar); + ath10k_mac_update_vif_chan(ar, vifs, n_vifs); mutex_unlock(&ar->conf_mutex); return 0; -- cgit v0.10.2 From 9713e3de801eb9c2dd2221ba8c1dddd5bc7ba360 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Thu, 3 Sep 2015 10:44:52 +0200 Subject: ath10k: handle IEEE80211_CHANCTX_CHANGE_WIDTH properly Vdevs associated with a given chanctx should be restarted if the bandwidth changes. Otherwise traffic may cease. This is known to fix STA CSA with bandwidths wider than 20MHz. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index eee5aa9..bb44161 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -6408,12 +6408,52 @@ ath10k_mac_op_remove_chanctx(struct ieee80211_hw *hw, mutex_unlock(&ar->conf_mutex); } +struct ath10k_mac_change_chanctx_arg { + struct ieee80211_chanctx_conf *ctx; + struct ieee80211_vif_chanctx_switch *vifs; + int n_vifs; + int next_vif; +}; + +static void +ath10k_mac_change_chanctx_cnt_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct ath10k_mac_change_chanctx_arg *arg = data; + + if (rcu_access_pointer(vif->chanctx_conf) != arg->ctx) + return; + + arg->n_vifs++; +} + +static void +ath10k_mac_change_chanctx_fill_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct ath10k_mac_change_chanctx_arg *arg = data; + struct ieee80211_chanctx_conf *ctx; + + ctx = rcu_access_pointer(vif->chanctx_conf); + if (ctx != arg->ctx) + return; + + if (WARN_ON(arg->next_vif == arg->n_vifs)) + return; + + arg->vifs[arg->next_vif].vif = vif; + arg->vifs[arg->next_vif].old_ctx = ctx; + arg->vifs[arg->next_vif].new_ctx = ctx; + arg->next_vif++; +} + static void ath10k_mac_op_change_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx, u32 changed) { struct ath10k *ar = hw->priv; + struct ath10k_mac_change_chanctx_arg arg = { .ctx = ctx }; mutex_lock(&ar->conf_mutex); @@ -6427,6 +6467,30 @@ ath10k_mac_op_change_chanctx(struct ieee80211_hw *hw, if (WARN_ON(changed & IEEE80211_CHANCTX_CHANGE_CHANNEL)) goto unlock; + if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH) { + ieee80211_iterate_active_interfaces_atomic( + hw, + IEEE80211_IFACE_ITER_NORMAL, + ath10k_mac_change_chanctx_cnt_iter, + &arg); + if (arg.n_vifs == 0) + goto radar; + + arg.vifs = kcalloc(arg.n_vifs, sizeof(arg.vifs[0]), + GFP_KERNEL); + if (!arg.vifs) + goto radar; + + ieee80211_iterate_active_interfaces_atomic( + hw, + IEEE80211_IFACE_ITER_NORMAL, + ath10k_mac_change_chanctx_fill_iter, + &arg); + ath10k_mac_update_vif_chan(ar, arg.vifs, arg.n_vifs); + kfree(arg.vifs); + } + +radar: ath10k_recalc_radar_detection(ar); /* FIXME: How to configure Rx chains properly? */ -- cgit v0.10.2 From 5af82fa66a7ee8dfc29fadb487a02e2ef14ea965 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 9 Sep 2015 11:34:37 +0300 Subject: ath10k: add ATH10K_FW_FEATURE_RAW_MODE_SUPPORT to ath10k_core_fw_feature_str[] This was missed in the original commit adding the flag and ath10k only printed "bit10": ath10k_pci 0000:02:00.0: qca988x hw2.0 (0x4100016c, 0x043202ff) fw 10.2.4.70.6-2 api 3 htt-ver 2.1 wmi-op 5 htt-op 2 cal otp max-sta 128 raw 0 hwcrypto 1 features no-p2p,bit10 Also add a build test to avoid this happening again. Fixes: ccec9038c721 ("ath10k: enable raw encap mode and software crypto engine") Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 09a74a8..879625a 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -150,12 +150,17 @@ static const char *const ath10k_core_fw_feature_str[] = { [ATH10K_FW_FEATURE_IGNORE_OTP_RESULT] = "ignore-otp", [ATH10K_FW_FEATURE_NO_NWIFI_DECAP_4ADDR_PADDING] = "no-4addr-pad", [ATH10K_FW_FEATURE_SUPPORTS_SKIP_CLOCK_INIT] = "skip-clock-init", + [ATH10K_FW_FEATURE_RAW_MODE_SUPPORT] = "raw-mode", }; static unsigned int ath10k_core_get_fw_feature_str(char *buf, size_t buf_len, enum ath10k_fw_features feat) { + /* make sure that ath10k_core_fw_feature_str[] gets updated */ + BUILD_BUG_ON(ARRAY_SIZE(ath10k_core_fw_feature_str) != + ATH10K_FW_FEATURE_COUNT); + if (feat >= ARRAY_SIZE(ath10k_core_fw_feature_str) || WARN_ON(!ath10k_core_fw_feature_str[feat])) { return scnprintf(buf, buf_len, "bit%d", feat); -- cgit v0.10.2 From e904cf6fe23022cde4e0ea9d41601411a315a3dc Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Sun, 6 Sep 2015 13:09:01 +0200 Subject: ath9k_htc: introduce support for different fw versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current kernel support only one fw name with theoretically only one fw version located in “firmware/htc_[9271|7010].fw”. Which is ok so far we have only one fw version (1.3). After we realised new fw 1.4, we faced compatibility problem which was decided to solve by firmware name and location: - new firmware is located now in firmware/ath9k_htc/htc_[9271|7010]-1.4.0.fw - old version 1.3 should be on old place, so old kernel have no issues with it. - new kernels including this patch should be able to try different supported (min..max) fw version. - new kernel should be able to support old fw location too. At least for now. At same time this patch will add new module option which should allow user to play with development fw version without replacing stable one. If user will set “ath9k_htc use_dev_fw=1” module will try to find firmware/ath9k_htc/htc_[9271|7010]-1.dev.0.fw first and if it fails, use stable version: for example...1.4.0.fw. Signed-off-by: Oleksij Rempel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 10c02f5..165dd20 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -17,12 +17,8 @@ #include #include "htc.h" -/* identify firmware images */ -#define FIRMWARE_AR7010_1_1 "htc_7010.fw" -#define FIRMWARE_AR9271 "htc_9271.fw" - -MODULE_FIRMWARE(FIRMWARE_AR7010_1_1); -MODULE_FIRMWARE(FIRMWARE_AR9271); +MODULE_FIRMWARE(HTC_7010_MODULE_FW); +MODULE_FIRMWARE(HTC_9271_MODULE_FW); static struct usb_device_id ath9k_hif_usb_ids[] = { { USB_DEVICE(0x0cf3, 0x9271) }, /* Atheros */ @@ -1080,12 +1076,88 @@ static void ath9k_hif_usb_firmware_fail(struct hif_device_usb *hif_dev) device_unlock(parent); } +static void ath9k_hif_usb_firmware_cb(const struct firmware *fw, void *context); + +/* taken from iwlwifi */ +static int ath9k_hif_request_firmware(struct hif_device_usb *hif_dev, + bool first) +{ + char index[8], *chip; + int ret; + + if (first) { + if (htc_use_dev_fw) { + hif_dev->fw_minor_index = FIRMWARE_MINOR_IDX_MAX + 1; + sprintf(index, "%s", "dev"); + } else { + hif_dev->fw_minor_index = FIRMWARE_MINOR_IDX_MAX; + sprintf(index, "%d", hif_dev->fw_minor_index); + } + } else { + hif_dev->fw_minor_index--; + sprintf(index, "%d", hif_dev->fw_minor_index); + } + + /* test for FW 1.3 */ + if (MAJOR_VERSION_REQ == 1 && hif_dev->fw_minor_index == 3) { + const char *filename; + + if (IS_AR7010_DEVICE(hif_dev->usb_device_id->driver_info)) + filename = FIRMWARE_AR7010_1_1; + else + filename = FIRMWARE_AR9271; + + /* expected fw locations: + * - htc_9271.fw (stable version 1.3, depricated) + */ + snprintf(hif_dev->fw_name, sizeof(hif_dev->fw_name), + "%s", filename); + + } else if (hif_dev->fw_minor_index < FIRMWARE_MINOR_IDX_MIN) { + dev_err(&hif_dev->udev->dev, "no suitable firmware found!\n"); + + return -ENOENT; + } else { + if (IS_AR7010_DEVICE(hif_dev->usb_device_id->driver_info)) + chip = "7010"; + else + chip = "9271"; + + /* expected fw locations: + * - ath9k_htc/htc_9271-1.dev.0.fw (development version) + * - ath9k_htc/htc_9271-1.4.0.fw (stable version) + */ + snprintf(hif_dev->fw_name, sizeof(hif_dev->fw_name), + "%s/htc_%s-%d.%s.0.fw", HTC_FW_PATH, + chip, MAJOR_VERSION_REQ, index); + } + + ret = request_firmware_nowait(THIS_MODULE, true, hif_dev->fw_name, + &hif_dev->udev->dev, GFP_KERNEL, + hif_dev, ath9k_hif_usb_firmware_cb); + if (ret) { + dev_err(&hif_dev->udev->dev, + "ath9k_htc: Async request for firmware %s failed\n", + hif_dev->fw_name); + return ret; + } + + dev_info(&hif_dev->udev->dev, "ath9k_htc: Firmware %s requested\n", + hif_dev->fw_name); + + return ret; +} + static void ath9k_hif_usb_firmware_cb(const struct firmware *fw, void *context) { struct hif_device_usb *hif_dev = context; int ret; if (!fw) { + ret = ath9k_hif_request_firmware(hif_dev, false); + if (!ret) + return; + dev_err(&hif_dev->udev->dev, "ath9k_htc: Failed to get firmware %s\n", hif_dev->fw_name); @@ -1215,27 +1287,11 @@ static int ath9k_hif_usb_probe(struct usb_interface *interface, init_completion(&hif_dev->fw_done); - /* Find out which firmware to load */ - - if (IS_AR7010_DEVICE(id->driver_info)) - hif_dev->fw_name = FIRMWARE_AR7010_1_1; - else - hif_dev->fw_name = FIRMWARE_AR9271; - - ret = request_firmware_nowait(THIS_MODULE, true, hif_dev->fw_name, - &hif_dev->udev->dev, GFP_KERNEL, - hif_dev, ath9k_hif_usb_firmware_cb); - if (ret) { - dev_err(&hif_dev->udev->dev, - "ath9k_htc: Async request for firmware %s failed\n", - hif_dev->fw_name); + ret = ath9k_hif_request_firmware(hif_dev, true); + if (ret) goto err_fw_req; - } - dev_info(&hif_dev->udev->dev, "ath9k_htc: Firmware %s requested\n", - hif_dev->fw_name); - - return 0; + return ret; err_fw_req: usb_set_intfdata(interface, NULL); diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.h b/drivers/net/wireless/ath/ath9k/hif_usb.h index 51496e7..7c2ef7e 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.h +++ b/drivers/net/wireless/ath/ath9k/hif_usb.h @@ -17,8 +17,26 @@ #ifndef HTC_USB_H #define HTC_USB_H +/* old firmware images */ +#define FIRMWARE_AR7010_1_1 "htc_7010.fw" +#define FIRMWARE_AR9271 "htc_9271.fw" + +/* supported Major FW version */ #define MAJOR_VERSION_REQ 1 #define MINOR_VERSION_REQ 3 +/* minimal and maximal supported Minor FW version. */ +#define FIRMWARE_MINOR_IDX_MAX 4 +#define FIRMWARE_MINOR_IDX_MIN 3 +#define HTC_FW_PATH "ath9k_htc" + +#define HTC_9271_MODULE_FW HTC_FW_PATH "/htc_9271-" \ + __stringify(MAJOR_VERSION_REQ) \ + "." __stringify(FIRMWARE_MINOR_IDX_MAX) ".0.fw" +#define HTC_7010_MODULE_FW HTC_FW_PATH "/htc_7010-" \ + __stringify(MAJOR_VERSION_REQ) \ + "." __stringify(FIRMWARE_MINOR_IDX_MAX) ".0.fw" + +extern int htc_use_dev_fw; #define IS_AR7010_DEVICE(_v) (((_v) == AR9280_USB) || ((_v) == AR9287_USB)) @@ -101,7 +119,8 @@ struct hif_device_usb { struct usb_anchor reg_in_submitted; struct usb_anchor mgmt_submitted; struct sk_buff *remain_skb; - const char *fw_name; + char fw_name[32]; + int fw_minor_index; int rx_remain_len; int rx_pkt_len; int rx_transfer_len; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index 1e84882..efe77db 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -38,6 +38,10 @@ static int ath9k_ps_enable; module_param_named(ps_enable, ath9k_ps_enable, int, 0444); MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave"); +int htc_use_dev_fw = 0; +module_param_named(use_dev_fw, htc_use_dev_fw, int, 0444); +MODULE_PARM_DESC(use_dev_fw, "Use development FW version"); + #ifdef CONFIG_MAC80211_LEDS int ath9k_htc_led_blink = 1; module_param_named(blink, ath9k_htc_led_blink, int, 0444); -- cgit v0.10.2 From 89ced540eb3527669bac0a3239a408fb1129f0dc Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 26 Aug 2015 13:50:59 +0300 Subject: iwlwifi: mvm: remove IWL_UCODE_TLV_API_HDC_PHASE_0 TLV flag All the supported firwmares support the new API. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luciano Coelho Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h index 483b1274..55ff72e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h @@ -247,7 +247,6 @@ typedef unsigned int __bitwise__ iwl_ucode_tlv_api_t; * @IWL_UCODE_TLV_API_FRAGMENTED_SCAN: This ucode supports active dwell time * longer than the passive one, which is essential for fragmented scan. * @IWL_UCODE_TLV_API_WIFI_MCC_UPDATE: ucode supports MCC updates with source. - * IWL_UCODE_TLV_API_HDC_PHASE_0: ucode supports finer configuration of LTR * @IWL_UCODE_TLV_API_TX_POWER_DEV: new API for tx power. * @IWL_UCODE_TLV_API_WIDE_CMD_HDR: ucode supports wide command header * @IWL_UCODE_TLV_API_SINGLE_SCAN_EBS: EBS is supported for single scans too. @@ -264,7 +263,6 @@ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_BT_COEX_SPLIT = (__force iwl_ucode_tlv_api_t)3, IWL_UCODE_TLV_API_FRAGMENTED_SCAN = (__force iwl_ucode_tlv_api_t)8, IWL_UCODE_TLV_API_WIFI_MCC_UPDATE = (__force iwl_ucode_tlv_api_t)9, - IWL_UCODE_TLV_API_HDC_PHASE_0 = (__force iwl_ucode_tlv_api_t)10, IWL_UCODE_TLV_API_TX_POWER_DEV = (__force iwl_ucode_tlv_api_t)11, IWL_UCODE_TLV_API_WIDE_CMD_HDR = (__force iwl_ucode_tlv_api_t)14, IWL_UCODE_TLV_API_SINGLE_SCAN_EBS = (__force iwl_ucode_tlv_api_t)16, diff --git a/drivers/net/wireless/iwlwifi/iwl-io.c b/drivers/net/wireless/iwlwifi/iwl-io.c index 3c263a1..0bd9d4a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/iwlwifi/iwl-io.c @@ -53,6 +53,7 @@ IWL_EXPORT_SYMBOL(iwl_write32); u32 iwl_read32(struct iwl_trans *trans, u32 ofs) { u32 val = iwl_trans_read32(trans, ofs); + trace_iwlwifi_dev_ioread32(trans->dev, ofs, val); return val; } diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c index 4a0ce83..248b025 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/iwlwifi/mvm/fw.c @@ -940,19 +940,6 @@ int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id) return ret; } -static int iwl_mvm_config_ltr_v1(struct iwl_mvm *mvm) -{ - struct iwl_ltr_config_cmd_v1 cmd_v1 = { - .flags = cpu_to_le32(LTR_CFG_FLAG_FEATURE_ENABLE), - }; - - if (!mvm->trans->ltr_enabled) - return 0; - - return iwl_mvm_send_cmd_pdu(mvm, LTR_CONFIG, 0, - sizeof(cmd_v1), &cmd_v1); -} - static int iwl_mvm_config_ltr(struct iwl_mvm *mvm) { struct iwl_ltr_config_cmd cmd = { @@ -962,9 +949,6 @@ static int iwl_mvm_config_ltr(struct iwl_mvm *mvm) if (!mvm->trans->ltr_enabled) return 0; - if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_HDC_PHASE_0)) - return iwl_mvm_config_ltr_v1(mvm); - return iwl_mvm_send_cmd_pdu(mvm, LTR_CONFIG, 0, sizeof(cmd), &cmd); } -- cgit v0.10.2 From 4d31eed13f2a2234e86aa178918e129b6e33a9ee Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 26 Aug 2015 13:52:54 +0300 Subject: iwlwifi: mvm: remove IWL_UCODE_TLV_API_TX_POWER_DEV TLV flag All the supported firmwares use the new API. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h index 55ff72e..7af0ec2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h @@ -247,7 +247,6 @@ typedef unsigned int __bitwise__ iwl_ucode_tlv_api_t; * @IWL_UCODE_TLV_API_FRAGMENTED_SCAN: This ucode supports active dwell time * longer than the passive one, which is essential for fragmented scan. * @IWL_UCODE_TLV_API_WIFI_MCC_UPDATE: ucode supports MCC updates with source. - * @IWL_UCODE_TLV_API_TX_POWER_DEV: new API for tx power. * @IWL_UCODE_TLV_API_WIDE_CMD_HDR: ucode supports wide command header * @IWL_UCODE_TLV_API_SINGLE_SCAN_EBS: EBS is supported for single scans too. * @IWL_UCODE_TLV_API_ASYNC_DTM: Async temperature notifications are supported. @@ -263,7 +262,6 @@ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_BT_COEX_SPLIT = (__force iwl_ucode_tlv_api_t)3, IWL_UCODE_TLV_API_FRAGMENTED_SCAN = (__force iwl_ucode_tlv_api_t)8, IWL_UCODE_TLV_API_WIFI_MCC_UPDATE = (__force iwl_ucode_tlv_api_t)9, - IWL_UCODE_TLV_API_TX_POWER_DEV = (__force iwl_ucode_tlv_api_t)11, IWL_UCODE_TLV_API_WIDE_CMD_HDR = (__force iwl_ucode_tlv_api_t)14, IWL_UCODE_TLV_API_SINGLE_SCAN_EBS = (__force iwl_ucode_tlv_api_t)16, IWL_UCODE_TLV_API_ASYNC_DTM = (__force iwl_ucode_tlv_api_t)17, diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index f9f88e9..4c497fe 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -1577,20 +1577,6 @@ static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm) return NULL; } -static int iwl_mvm_set_tx_power_old(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, s8 tx_power) -{ - /* FW is in charge of regulatory enforcement */ - struct iwl_reduce_tx_power_cmd reduce_txpwr_cmd = { - .mac_context_id = iwl_mvm_vif_from_mac80211(vif)->id, - .pwr_restriction = cpu_to_le16(tx_power), - }; - - return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, - sizeof(reduce_txpwr_cmd), - &reduce_txpwr_cmd); -} - static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif, s16 tx_power) { @@ -1602,9 +1588,6 @@ static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif, }; int len = sizeof(cmd); - if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_TX_POWER_DEV)) - return iwl_mvm_set_tx_power_old(mvm, vif, tx_power); - if (tx_power == IWL_DEFAULT_MAX_TX_POWER) cmd.v2.pwr_restriction = cpu_to_le16(IWL_DEV_MAX_TX_POWER); -- cgit v0.10.2 From eb991c5ec18ba41bfa2c206c22bceae94f6f3c15 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 26 Aug 2015 13:55:11 +0300 Subject: iwlwifi: mvm: remove IWL_UCODE_TLV_API_SINGLE_SCAN_EBS TLV flag All the supported firmwares have this flag set. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h index 7af0ec2..880962e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h @@ -248,7 +248,6 @@ typedef unsigned int __bitwise__ iwl_ucode_tlv_api_t; * longer than the passive one, which is essential for fragmented scan. * @IWL_UCODE_TLV_API_WIFI_MCC_UPDATE: ucode supports MCC updates with source. * @IWL_UCODE_TLV_API_WIDE_CMD_HDR: ucode supports wide command header - * @IWL_UCODE_TLV_API_SINGLE_SCAN_EBS: EBS is supported for single scans too. * @IWL_UCODE_TLV_API_ASYNC_DTM: Async temperature notifications are supported. * @IWL_UCODE_TLV_API_LQ_SS_PARAMS: Configure STBC/BFER via LQ CMD ss_params * @IWL_UCODE_TLV_API_STATS_V10: uCode supports/uses statistics API version 10 @@ -263,7 +262,6 @@ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_FRAGMENTED_SCAN = (__force iwl_ucode_tlv_api_t)8, IWL_UCODE_TLV_API_WIFI_MCC_UPDATE = (__force iwl_ucode_tlv_api_t)9, IWL_UCODE_TLV_API_WIDE_CMD_HDR = (__force iwl_ucode_tlv_api_t)14, - IWL_UCODE_TLV_API_SINGLE_SCAN_EBS = (__force iwl_ucode_tlv_api_t)16, IWL_UCODE_TLV_API_ASYNC_DTM = (__force iwl_ucode_tlv_api_t)17, IWL_UCODE_TLV_API_LQ_SS_PARAMS = (__force iwl_ucode_tlv_api_t)18, IWL_UCODE_TLV_API_STATS_V10 = (__force iwl_ucode_tlv_api_t)19, diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index 56559d4..4a1f9af 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -750,8 +750,6 @@ static inline bool iwl_mvm_scan_use_ebs(struct iwl_mvm *mvm, */ return ((capa->flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT) && mvm->last_ebs_successful && - (n_iterations > 1 || - fw_has_api(capa, IWL_UCODE_TLV_API_SINGLE_SCAN_EBS)) && vif->type != NL80211_IFTYPE_P2P_DEVICE); } -- cgit v0.10.2 From 9d43fa4b4a0e46881d7d4fb3bc689a17d2f72257 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 26 Aug 2015 13:57:37 +0300 Subject: iwlwifi: mvm: remove IWL_UCODE_TLV_API_ASYNC_DTM TLV flag This flag is set in all supported firmwares. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h index 880962e..104468b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h @@ -248,7 +248,6 @@ typedef unsigned int __bitwise__ iwl_ucode_tlv_api_t; * longer than the passive one, which is essential for fragmented scan. * @IWL_UCODE_TLV_API_WIFI_MCC_UPDATE: ucode supports MCC updates with source. * @IWL_UCODE_TLV_API_WIDE_CMD_HDR: ucode supports wide command header - * @IWL_UCODE_TLV_API_ASYNC_DTM: Async temperature notifications are supported. * @IWL_UCODE_TLV_API_LQ_SS_PARAMS: Configure STBC/BFER via LQ CMD ss_params * @IWL_UCODE_TLV_API_STATS_V10: uCode supports/uses statistics API version 10 * @IWL_UCODE_TLV_API_NEW_VERSION: new versioning format @@ -262,7 +261,6 @@ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_FRAGMENTED_SCAN = (__force iwl_ucode_tlv_api_t)8, IWL_UCODE_TLV_API_WIFI_MCC_UPDATE = (__force iwl_ucode_tlv_api_t)9, IWL_UCODE_TLV_API_WIDE_CMD_HDR = (__force iwl_ucode_tlv_api_t)14, - IWL_UCODE_TLV_API_ASYNC_DTM = (__force iwl_ucode_tlv_api_t)17, IWL_UCODE_TLV_API_LQ_SS_PARAMS = (__force iwl_ucode_tlv_api_t)18, IWL_UCODE_TLV_API_STATS_V10 = (__force iwl_ucode_tlv_api_t)19, IWL_UCODE_TLV_API_NEW_VERSION = (__force iwl_ucode_tlv_api_t)20, diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c index 2ad814d..b620142 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/iwlwifi/mvm/rx.c @@ -621,12 +621,6 @@ void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm, iwl_mvm_rx_stats_check_trigger(mvm, pkt); - /* Only handle rx statistics temperature changes if async temp - * notifications are not supported - */ - if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_ASYNC_DTM)) - iwl_mvm_tt_temp_changed(mvm, temperature); - ieee80211_iterate_active_interfaces(mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_stat_iterator, -- cgit v0.10.2 From 38d5f66f062a65bfc436013135a817e53037ccca Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 26 Aug 2015 14:03:02 +0300 Subject: iwlwifi: mvm: remove IWL_UCODE_TLV_API_STATS_V10 TLV flag This flag is set in all supported firmwares. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h index 104468b..352d245 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h @@ -249,7 +249,6 @@ typedef unsigned int __bitwise__ iwl_ucode_tlv_api_t; * @IWL_UCODE_TLV_API_WIFI_MCC_UPDATE: ucode supports MCC updates with source. * @IWL_UCODE_TLV_API_WIDE_CMD_HDR: ucode supports wide command header * @IWL_UCODE_TLV_API_LQ_SS_PARAMS: Configure STBC/BFER via LQ CMD ss_params - * @IWL_UCODE_TLV_API_STATS_V10: uCode supports/uses statistics API version 10 * @IWL_UCODE_TLV_API_NEW_VERSION: new versioning format * @IWL_UCODE_TLV_API_EXT_SCAN_PRIORITY: scan APIs use 8-level priority * instead of 3. @@ -262,7 +261,6 @@ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_WIFI_MCC_UPDATE = (__force iwl_ucode_tlv_api_t)9, IWL_UCODE_TLV_API_WIDE_CMD_HDR = (__force iwl_ucode_tlv_api_t)14, IWL_UCODE_TLV_API_LQ_SS_PARAMS = (__force iwl_ucode_tlv_api_t)18, - IWL_UCODE_TLV_API_STATS_V10 = (__force iwl_ucode_tlv_api_t)19, IWL_UCODE_TLV_API_NEW_VERSION = (__force iwl_ucode_tlv_api_t)20, IWL_UCODE_TLV_API_EXT_SCAN_PRIORITY = (__force iwl_ucode_tlv_api_t)24, IWL_UCODE_TLV_API_TX_POWER_CHAIN = (__force iwl_ucode_tlv_api_t)27, diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h index 4ca4bce..0c321f6 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h @@ -219,32 +219,6 @@ struct mvm_statistics_bt_activity { __le32 lo_priority_rx_denied_cnt; } __packed; /* STATISTICS_BT_ACTIVITY_API_S_VER_1 */ -struct mvm_statistics_general_v5 { - __le32 radio_temperature; - __le32 radio_voltage; - struct mvm_statistics_dbg dbg; - __le32 sleep_time; - __le32 slots_out; - __le32 slots_idle; - __le32 ttl_timestamp; - struct mvm_statistics_div slow_div; - __le32 rx_enable_counter; - /* - * num_of_sos_states: - * count the number of times we have to re-tune - * in order to get out of bad PHY status - */ - __le32 num_of_sos_states; - __le32 beacon_filtered; - __le32 missed_beacons; - u8 beacon_filter_average_energy; - u8 beacon_filter_reason; - u8 beacon_filter_current_energy; - u8 beacon_filter_reserved; - __le32 beacon_filter_delta_time; - struct mvm_statistics_bt_activity bt_activity; -} __packed; /* STATISTICS_GENERAL_API_S_VER_5 */ - struct mvm_statistics_general_v8 { __le32 radio_temperature; __le32 radio_voltage; @@ -293,13 +267,6 @@ struct mvm_statistics_rx { * STATISTICS_CMD (0x9c), below. */ -struct iwl_notif_statistics_v8 { - __le32 flag; - struct mvm_statistics_rx rx; - struct mvm_statistics_tx tx; - struct mvm_statistics_general_v5 general; -} __packed; /* STATISTICS_NTFY_API_S_VER_8 */ - struct iwl_notif_statistics_v10 { __le32 flag; struct mvm_statistics_rx rx; diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c index b620142..c203a1b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/iwlwifi/mvm/rx.c @@ -577,47 +577,30 @@ iwl_mvm_rx_stats_check_trigger(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) { - size_t v8_len = sizeof(struct iwl_notif_statistics_v8); - size_t v10_len = sizeof(struct iwl_notif_statistics_v10); + struct iwl_notif_statistics_v10 *stats = (void *)&pkt->data; struct iwl_mvm_stat_data data = { .mvm = mvm, }; u32 temperature; - if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_STATS_V10)) { - struct iwl_notif_statistics_v10 *stats = (void *)&pkt->data; + if (iwl_rx_packet_payload_len(pkt) != sizeof(*stats)) + goto invalid; - if (iwl_rx_packet_payload_len(pkt) != v10_len) - goto invalid; + temperature = le32_to_cpu(stats->general.radio_temperature); + data.mac_id = stats->rx.general.mac_id; + data.beacon_filter_average_energy = + stats->general.beacon_filter_average_energy; - temperature = le32_to_cpu(stats->general.radio_temperature); - data.mac_id = stats->rx.general.mac_id; - data.beacon_filter_average_energy = - stats->general.beacon_filter_average_energy; + iwl_mvm_update_rx_statistics(mvm, &stats->rx); - iwl_mvm_update_rx_statistics(mvm, &stats->rx); + mvm->radio_stats.rx_time = le64_to_cpu(stats->general.rx_time); + mvm->radio_stats.tx_time = le64_to_cpu(stats->general.tx_time); + mvm->radio_stats.on_time_rf = + le64_to_cpu(stats->general.on_time_rf); + mvm->radio_stats.on_time_scan = + le64_to_cpu(stats->general.on_time_scan); - mvm->radio_stats.rx_time = le64_to_cpu(stats->general.rx_time); - mvm->radio_stats.tx_time = le64_to_cpu(stats->general.tx_time); - mvm->radio_stats.on_time_rf = - le64_to_cpu(stats->general.on_time_rf); - mvm->radio_stats.on_time_scan = - le64_to_cpu(stats->general.on_time_scan); - - data.general = &stats->general; - } else { - struct iwl_notif_statistics_v8 *stats = (void *)&pkt->data; - - if (iwl_rx_packet_payload_len(pkt) != v8_len) - goto invalid; - - temperature = le32_to_cpu(stats->general.radio_temperature); - data.mac_id = stats->rx.general.mac_id; - data.beacon_filter_average_energy = - stats->general.beacon_filter_average_energy; - - iwl_mvm_update_rx_statistics(mvm, &stats->rx); - } + data.general = &stats->general; iwl_mvm_rx_stats_check_trigger(mvm, pkt); -- cgit v0.10.2 From da583fdfececf69571d3b8fafed278310592eec1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 26 Aug 2015 15:55:04 +0200 Subject: iwlwifi: mvm: make sure AP is operating for ToF It's possible for an AP interface to be UP but not actually operating (i.e. not beaconing etc.) - in this case it can't actually do ToF, so check for it. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/tof.c b/drivers/net/wireless/iwlwifi/mvm/tof.c index 380972f..7ae0bd8 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tof.c +++ b/drivers/net/wireless/iwlwifi/mvm/tof.c @@ -178,7 +178,8 @@ int iwl_mvm_tof_responder_cmd(struct iwl_mvm *mvm, if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT)) return -EINVAL; - if (vif->p2p || vif->type != NL80211_IFTYPE_AP) { + if (vif->p2p || vif->type != NL80211_IFTYPE_AP || + !mvmvif->ap_ibss_active) { IWL_ERR(mvm, "Cannot start responder, not in AP mode\n"); return -EIO; } -- cgit v0.10.2 From 7f89a58efc0a18ec4a6eb7fdcf9ab73d426ce11f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 2 Sep 2015 11:51:23 +0200 Subject: iwlwifi: mvm: remove useless debug message from RX This message is useless - it's in the good case that always happens so enabling it doesn't really help. Just remove it. There are other ways to debug this (e.g. tracing) so there's no need to add a message in the bad case. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c index c203a1b..623c219 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/iwlwifi/mvm/rx.c @@ -202,7 +202,6 @@ static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm, return -1; stats->flag |= RX_FLAG_DECRYPTED; - IWL_DEBUG_WEP(mvm, "hw decrypted CCMP successfully\n"); *crypt_len = IEEE80211_CCMP_HDR_LEN; return 0; -- cgit v0.10.2 From abfd794c59c7cbfe31a5d1471e5868d4dbd74205 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 2 Sep 2015 12:26:45 +0200 Subject: iwlwifi: mvm: remove pointless cfg_phy_cnt length check Since the driver can never configure the data here, this field will always be reported as 0 by the firmware. Even if this was not the case, however, it wouldn't matter since the extra data would be added beyond the end of the phy_info structure we use in the driver, so wouldn't harm anything in this code either. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c index 623c219..0a6d47c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/iwlwifi/mvm/rx.c @@ -298,13 +298,6 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, return; } - if ((unlikely(phy_info->cfg_phy_cnt > 20))) { - IWL_DEBUG_DROP(mvm, "dsp size out of range [0,20]: %d\n", - phy_info->cfg_phy_cnt); - kfree_skb(skb); - return; - } - /* * Keep packets with CRC errors (and with overrun) for monitor mode * (otherwise the firmware discards them) but mark them as bad. -- cgit v0.10.2 From 2df5328e7853e33155312970036f05c27c189b71 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 2 Sep 2015 14:47:16 +0200 Subject: iwlwifi: mvm: remove some unused defines from RX API Remove some unused values from the RX API; these were used with older firmware API that didn't have the RX energy API, support for which was removed a long time ago. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index cd293d2..54db515 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h @@ -1080,23 +1080,6 @@ struct iwl_hs20_roc_res { #define IWL_RX_INFO_ENERGY_ANT_B_POS 8 #define IWL_RX_INFO_ENERGY_ANT_C_POS 16 -#define IWL_RX_INFO_AGC_IDX 1 -#define IWL_RX_INFO_RSSI_AB_IDX 2 -#define IWL_OFDM_AGC_A_MSK 0x0000007f -#define IWL_OFDM_AGC_A_POS 0 -#define IWL_OFDM_AGC_B_MSK 0x00003f80 -#define IWL_OFDM_AGC_B_POS 7 -#define IWL_OFDM_AGC_CODE_MSK 0x3fe00000 -#define IWL_OFDM_AGC_CODE_POS 20 -#define IWL_OFDM_RSSI_INBAND_A_MSK 0x00ff -#define IWL_OFDM_RSSI_A_POS 0 -#define IWL_OFDM_RSSI_ALLBAND_A_MSK 0xff00 -#define IWL_OFDM_RSSI_ALLBAND_A_POS 8 -#define IWL_OFDM_RSSI_INBAND_B_MSK 0xff0000 -#define IWL_OFDM_RSSI_B_POS 16 -#define IWL_OFDM_RSSI_ALLBAND_B_MSK 0xff000000 -#define IWL_OFDM_RSSI_ALLBAND_B_POS 24 - /** * struct iwl_rx_phy_info - phy info * (REPLY_RX_PHY_CMD = 0xc0) -- cgit v0.10.2 From ee6dbb29377f6055093cccefc0cd0ac2670b12b3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 2 Sep 2015 14:53:39 +0200 Subject: iwlwifi: mvm: move RX API into its own file The RX API is currently mixed up into the general fw-api.h file, but we're going to need to extend it significantly in the future, so move it to its own file. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-rx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-rx.h new file mode 100644 index 0000000..9b7e49d --- /dev/null +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-rx.h @@ -0,0 +1,238 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH + * + * 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. + * + * 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 COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH + * 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 Intel Corporation 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 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 __fw_api_rx_h__ +#define __fw_api_rx_h__ + +#define IWL_RX_INFO_PHY_CNT 8 +#define IWL_RX_INFO_ENERGY_ANT_ABC_IDX 1 +#define IWL_RX_INFO_ENERGY_ANT_A_MSK 0x000000ff +#define IWL_RX_INFO_ENERGY_ANT_B_MSK 0x0000ff00 +#define IWL_RX_INFO_ENERGY_ANT_C_MSK 0x00ff0000 +#define IWL_RX_INFO_ENERGY_ANT_A_POS 0 +#define IWL_RX_INFO_ENERGY_ANT_B_POS 8 +#define IWL_RX_INFO_ENERGY_ANT_C_POS 16 + +/** + * struct iwl_rx_phy_info - phy info + * (REPLY_RX_PHY_CMD = 0xc0) + * @non_cfg_phy_cnt: non configurable DSP phy data byte count + * @cfg_phy_cnt: configurable DSP phy data byte count + * @stat_id: configurable DSP phy data set ID + * @reserved1: + * @system_timestamp: GP2 at on air rise + * @timestamp: TSF at on air rise + * @beacon_time_stamp: beacon at on-air rise + * @phy_flags: general phy flags: band, modulation, ... + * @channel: channel number + * @non_cfg_phy_buf: for various implementations of non_cfg_phy + * @rate_n_flags: RATE_MCS_* + * @byte_count: frame's byte-count + * @frame_time: frame's time on the air, based on byte count and frame rate + * calculation + * @mac_active_msk: what MACs were active when the frame was received + * + * Before each Rx, the device sends this data. It contains PHY information + * about the reception of the packet. + */ +struct iwl_rx_phy_info { + u8 non_cfg_phy_cnt; + u8 cfg_phy_cnt; + u8 stat_id; + u8 reserved1; + __le32 system_timestamp; + __le64 timestamp; + __le32 beacon_time_stamp; + __le16 phy_flags; + __le16 channel; + __le32 non_cfg_phy[IWL_RX_INFO_PHY_CNT]; + __le32 rate_n_flags; + __le32 byte_count; + __le16 mac_active_msk; + __le16 frame_time; +} __packed; + +/* + * TCP offload Rx assist info + * + * bits 0:3 - reserved + * bits 4:7 - MIC CRC length + * bits 8:12 - MAC header length + * bit 13 - Padding indication + * bit 14 - A-AMSDU indication + * bit 15 - Offload enabled + */ +enum iwl_csum_rx_assist_info { + CSUM_RXA_RESERVED_MASK = 0x000f, + CSUM_RXA_MICSIZE_MASK = 0x00f0, + CSUM_RXA_HEADERLEN_MASK = 0x1f00, + CSUM_RXA_PADD = BIT(13), + CSUM_RXA_AMSDU = BIT(14), + CSUM_RXA_ENA = BIT(15) +}; + +/** + * struct iwl_rx_mpdu_res_start - phy info + * @assist: see CSUM_RX_ASSIST_ above + */ +struct iwl_rx_mpdu_res_start { + __le16 byte_count; + __le16 assist; +} __packed; /* _RX_MPDU_RES_START_API_S_VER_2 */ + +/** + * enum iwl_rx_phy_flags - to parse %iwl_rx_phy_info phy_flags + * @RX_RES_PHY_FLAGS_BAND_24: true if the packet was received on 2.4 band + * @RX_RES_PHY_FLAGS_MOD_CCK: + * @RX_RES_PHY_FLAGS_SHORT_PREAMBLE: true if packet's preamble was short + * @RX_RES_PHY_FLAGS_NARROW_BAND: + * @RX_RES_PHY_FLAGS_ANTENNA: antenna on which the packet was received + * @RX_RES_PHY_FLAGS_AGG: set if the packet was part of an A-MPDU + * @RX_RES_PHY_FLAGS_OFDM_HT: The frame was an HT frame + * @RX_RES_PHY_FLAGS_OFDM_GF: The frame used GF preamble + * @RX_RES_PHY_FLAGS_OFDM_VHT: The frame was a VHT frame + */ +enum iwl_rx_phy_flags { + RX_RES_PHY_FLAGS_BAND_24 = BIT(0), + RX_RES_PHY_FLAGS_MOD_CCK = BIT(1), + RX_RES_PHY_FLAGS_SHORT_PREAMBLE = BIT(2), + RX_RES_PHY_FLAGS_NARROW_BAND = BIT(3), + RX_RES_PHY_FLAGS_ANTENNA = (0x7 << 4), + RX_RES_PHY_FLAGS_ANTENNA_POS = 4, + RX_RES_PHY_FLAGS_AGG = BIT(7), + RX_RES_PHY_FLAGS_OFDM_HT = BIT(8), + RX_RES_PHY_FLAGS_OFDM_GF = BIT(9), + RX_RES_PHY_FLAGS_OFDM_VHT = BIT(10), +}; + +/** + * enum iwl_mvm_rx_status - written by fw for each Rx packet + * @RX_MPDU_RES_STATUS_CRC_OK: CRC is fine + * @RX_MPDU_RES_STATUS_OVERRUN_OK: there was no RXE overflow + * @RX_MPDU_RES_STATUS_SRC_STA_FOUND: + * @RX_MPDU_RES_STATUS_KEY_VALID: + * @RX_MPDU_RES_STATUS_KEY_PARAM_OK: + * @RX_MPDU_RES_STATUS_ICV_OK: ICV is fine, if not, the packet is destroyed + * @RX_MPDU_RES_STATUS_MIC_OK: used for CCM alg only. TKIP MIC is checked + * in the driver. + * @RX_MPDU_RES_STATUS_TTAK_OK: TTAK is fine + * @RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR: valid for alg = CCM_CMAC or + * alg = CCM only. Checks replay attack for 11w frames. Relevant only if + * %RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME is set. + * @RX_MPDU_RES_STATUS_SEC_NO_ENC: this frame is not encrypted + * @RX_MPDU_RES_STATUS_SEC_WEP_ENC: this frame is encrypted using WEP + * @RX_MPDU_RES_STATUS_SEC_CCM_ENC: this frame is encrypted using CCM + * @RX_MPDU_RES_STATUS_SEC_TKIP_ENC: this frame is encrypted using TKIP + * @RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC: this frame is encrypted using CCM_CMAC + * @RX_MPDU_RES_STATUS_SEC_ENC_ERR: this frame couldn't be decrypted + * @RX_MPDU_RES_STATUS_SEC_ENC_MSK: bitmask of the encryption algorithm + * @RX_MPDU_RES_STATUS_DEC_DONE: this frame has been successfully decrypted + * @RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP: + * @RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP: + * @RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT: + * @RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME: this frame is an 11w management frame + * @RX_MPDU_RES_STATUS_CSUM_DONE: checksum was done by the hw + * @RX_MPDU_RES_STATUS_CSUM_OK: checksum found no errors + * @RX_MPDU_RES_STATUS_HASH_INDEX_MSK: + * @RX_MPDU_RES_STATUS_STA_ID_MSK: + * @RX_MPDU_RES_STATUS_RRF_KILL: + * @RX_MPDU_RES_STATUS_FILTERING_MSK: + * @RX_MPDU_RES_STATUS2_FILTERING_MSK: + */ +enum iwl_mvm_rx_status { + RX_MPDU_RES_STATUS_CRC_OK = BIT(0), + RX_MPDU_RES_STATUS_OVERRUN_OK = BIT(1), + RX_MPDU_RES_STATUS_SRC_STA_FOUND = BIT(2), + RX_MPDU_RES_STATUS_KEY_VALID = BIT(3), + RX_MPDU_RES_STATUS_KEY_PARAM_OK = BIT(4), + RX_MPDU_RES_STATUS_ICV_OK = BIT(5), + RX_MPDU_RES_STATUS_MIC_OK = BIT(6), + RX_MPDU_RES_STATUS_TTAK_OK = BIT(7), + RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR = BIT(7), + RX_MPDU_RES_STATUS_SEC_NO_ENC = (0 << 8), + RX_MPDU_RES_STATUS_SEC_WEP_ENC = (1 << 8), + RX_MPDU_RES_STATUS_SEC_CCM_ENC = (2 << 8), + RX_MPDU_RES_STATUS_SEC_TKIP_ENC = (3 << 8), + RX_MPDU_RES_STATUS_SEC_EXT_ENC = (4 << 8), + RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC = (6 << 8), + RX_MPDU_RES_STATUS_SEC_ENC_ERR = (7 << 8), + RX_MPDU_RES_STATUS_SEC_ENC_MSK = (7 << 8), + RX_MPDU_RES_STATUS_DEC_DONE = BIT(11), + RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP = BIT(12), + RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP = BIT(13), + RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT = BIT(14), + RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME = BIT(15), + RX_MPDU_RES_STATUS_CSUM_DONE = BIT(16), + RX_MPDU_RES_STATUS_CSUM_OK = BIT(17), + RX_MPDU_RES_STATUS_HASH_INDEX_MSK = (0x3F0000), + RX_MPDU_RES_STATUS_STA_ID_MSK = (0x1f000000), + RX_MPDU_RES_STATUS_RRF_KILL = BIT(29), + RX_MPDU_RES_STATUS_FILTERING_MSK = (0xc00000), + RX_MPDU_RES_STATUS2_FILTERING_MSK = (0xc0000000), +}; + +#endif /* __fw_api_rx_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index 54db515..5e4014a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h @@ -67,6 +67,7 @@ #define __fw_api_h__ #include "fw-api-rs.h" +#include "fw-api-rx.h" #include "fw-api-tx.h" #include "fw-api-sta.h" #include "fw-api-mac.h" @@ -1071,173 +1072,6 @@ struct iwl_hs20_roc_res { __le32 status; } __packed; /* HOT_SPOT_RSP_API_S_VER_1 */ -#define IWL_RX_INFO_PHY_CNT 8 -#define IWL_RX_INFO_ENERGY_ANT_ABC_IDX 1 -#define IWL_RX_INFO_ENERGY_ANT_A_MSK 0x000000ff -#define IWL_RX_INFO_ENERGY_ANT_B_MSK 0x0000ff00 -#define IWL_RX_INFO_ENERGY_ANT_C_MSK 0x00ff0000 -#define IWL_RX_INFO_ENERGY_ANT_A_POS 0 -#define IWL_RX_INFO_ENERGY_ANT_B_POS 8 -#define IWL_RX_INFO_ENERGY_ANT_C_POS 16 - -/** - * struct iwl_rx_phy_info - phy info - * (REPLY_RX_PHY_CMD = 0xc0) - * @non_cfg_phy_cnt: non configurable DSP phy data byte count - * @cfg_phy_cnt: configurable DSP phy data byte count - * @stat_id: configurable DSP phy data set ID - * @reserved1: - * @system_timestamp: GP2 at on air rise - * @timestamp: TSF at on air rise - * @beacon_time_stamp: beacon at on-air rise - * @phy_flags: general phy flags: band, modulation, ... - * @channel: channel number - * @non_cfg_phy_buf: for various implementations of non_cfg_phy - * @rate_n_flags: RATE_MCS_* - * @byte_count: frame's byte-count - * @frame_time: frame's time on the air, based on byte count and frame rate - * calculation - * @mac_active_msk: what MACs were active when the frame was received - * - * Before each Rx, the device sends this data. It contains PHY information - * about the reception of the packet. - */ -struct iwl_rx_phy_info { - u8 non_cfg_phy_cnt; - u8 cfg_phy_cnt; - u8 stat_id; - u8 reserved1; - __le32 system_timestamp; - __le64 timestamp; - __le32 beacon_time_stamp; - __le16 phy_flags; - __le16 channel; - __le32 non_cfg_phy[IWL_RX_INFO_PHY_CNT]; - __le32 rate_n_flags; - __le32 byte_count; - __le16 mac_active_msk; - __le16 frame_time; -} __packed; - -/* - * TCP offload Rx assist info - * - * bits 0:3 - reserved - * bits 4:7 - MIC CRC length - * bits 8:12 - MAC header length - * bit 13 - Padding indication - * bit 14 - A-AMSDU indication - * bit 15 - Offload enabled - */ -enum iwl_csum_rx_assist_info { - CSUM_RXA_RESERVED_MASK = 0x000f, - CSUM_RXA_MICSIZE_MASK = 0x00f0, - CSUM_RXA_HEADERLEN_MASK = 0x1f00, - CSUM_RXA_PADD = BIT(13), - CSUM_RXA_AMSDU = BIT(14), - CSUM_RXA_ENA = BIT(15) -}; - -/** - * struct iwl_rx_mpdu_res_start - phy info - * @assist: see CSUM_RX_ASSIST_ above - */ -struct iwl_rx_mpdu_res_start { - __le16 byte_count; - __le16 assist; -} __packed; /* _RX_MPDU_RES_START_API_S_VER_2 */ - -/** - * enum iwl_rx_phy_flags - to parse %iwl_rx_phy_info phy_flags - * @RX_RES_PHY_FLAGS_BAND_24: true if the packet was received on 2.4 band - * @RX_RES_PHY_FLAGS_MOD_CCK: - * @RX_RES_PHY_FLAGS_SHORT_PREAMBLE: true if packet's preamble was short - * @RX_RES_PHY_FLAGS_NARROW_BAND: - * @RX_RES_PHY_FLAGS_ANTENNA: antenna on which the packet was received - * @RX_RES_PHY_FLAGS_AGG: set if the packet was part of an A-MPDU - * @RX_RES_PHY_FLAGS_OFDM_HT: The frame was an HT frame - * @RX_RES_PHY_FLAGS_OFDM_GF: The frame used GF preamble - * @RX_RES_PHY_FLAGS_OFDM_VHT: The frame was a VHT frame - */ -enum iwl_rx_phy_flags { - RX_RES_PHY_FLAGS_BAND_24 = BIT(0), - RX_RES_PHY_FLAGS_MOD_CCK = BIT(1), - RX_RES_PHY_FLAGS_SHORT_PREAMBLE = BIT(2), - RX_RES_PHY_FLAGS_NARROW_BAND = BIT(3), - RX_RES_PHY_FLAGS_ANTENNA = (0x7 << 4), - RX_RES_PHY_FLAGS_ANTENNA_POS = 4, - RX_RES_PHY_FLAGS_AGG = BIT(7), - RX_RES_PHY_FLAGS_OFDM_HT = BIT(8), - RX_RES_PHY_FLAGS_OFDM_GF = BIT(9), - RX_RES_PHY_FLAGS_OFDM_VHT = BIT(10), -}; - -/** - * enum iwl_mvm_rx_status - written by fw for each Rx packet - * @RX_MPDU_RES_STATUS_CRC_OK: CRC is fine - * @RX_MPDU_RES_STATUS_OVERRUN_OK: there was no RXE overflow - * @RX_MPDU_RES_STATUS_SRC_STA_FOUND: - * @RX_MPDU_RES_STATUS_KEY_VALID: - * @RX_MPDU_RES_STATUS_KEY_PARAM_OK: - * @RX_MPDU_RES_STATUS_ICV_OK: ICV is fine, if not, the packet is destroyed - * @RX_MPDU_RES_STATUS_MIC_OK: used for CCM alg only. TKIP MIC is checked - * in the driver. - * @RX_MPDU_RES_STATUS_TTAK_OK: TTAK is fine - * @RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR: valid for alg = CCM_CMAC or - * alg = CCM only. Checks replay attack for 11w frames. Relevant only if - * %RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME is set. - * @RX_MPDU_RES_STATUS_SEC_NO_ENC: this frame is not encrypted - * @RX_MPDU_RES_STATUS_SEC_WEP_ENC: this frame is encrypted using WEP - * @RX_MPDU_RES_STATUS_SEC_CCM_ENC: this frame is encrypted using CCM - * @RX_MPDU_RES_STATUS_SEC_TKIP_ENC: this frame is encrypted using TKIP - * @RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC: this frame is encrypted using CCM_CMAC - * @RX_MPDU_RES_STATUS_SEC_ENC_ERR: this frame couldn't be decrypted - * @RX_MPDU_RES_STATUS_SEC_ENC_MSK: bitmask of the encryption algorithm - * @RX_MPDU_RES_STATUS_DEC_DONE: this frame has been successfully decrypted - * @RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP: - * @RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP: - * @RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT: - * @RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME: this frame is an 11w management frame - * @RX_MPDU_RES_STATUS_CSUM_DONE: checksum was done by the hw - * @RX_MPDU_RES_STATUS_CSUM_OK: checksum found no errors - * @RX_MPDU_RES_STATUS_HASH_INDEX_MSK: - * @RX_MPDU_RES_STATUS_STA_ID_MSK: - * @RX_MPDU_RES_STATUS_RRF_KILL: - * @RX_MPDU_RES_STATUS_FILTERING_MSK: - * @RX_MPDU_RES_STATUS2_FILTERING_MSK: - */ -enum iwl_mvm_rx_status { - RX_MPDU_RES_STATUS_CRC_OK = BIT(0), - RX_MPDU_RES_STATUS_OVERRUN_OK = BIT(1), - RX_MPDU_RES_STATUS_SRC_STA_FOUND = BIT(2), - RX_MPDU_RES_STATUS_KEY_VALID = BIT(3), - RX_MPDU_RES_STATUS_KEY_PARAM_OK = BIT(4), - RX_MPDU_RES_STATUS_ICV_OK = BIT(5), - RX_MPDU_RES_STATUS_MIC_OK = BIT(6), - RX_MPDU_RES_STATUS_TTAK_OK = BIT(7), - RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR = BIT(7), - RX_MPDU_RES_STATUS_SEC_NO_ENC = (0 << 8), - RX_MPDU_RES_STATUS_SEC_WEP_ENC = (1 << 8), - RX_MPDU_RES_STATUS_SEC_CCM_ENC = (2 << 8), - RX_MPDU_RES_STATUS_SEC_TKIP_ENC = (3 << 8), - RX_MPDU_RES_STATUS_SEC_EXT_ENC = (4 << 8), - RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC = (6 << 8), - RX_MPDU_RES_STATUS_SEC_ENC_ERR = (7 << 8), - RX_MPDU_RES_STATUS_SEC_ENC_MSK = (7 << 8), - RX_MPDU_RES_STATUS_DEC_DONE = BIT(11), - RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP = BIT(12), - RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP = BIT(13), - RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT = BIT(14), - RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME = BIT(15), - RX_MPDU_RES_STATUS_CSUM_DONE = BIT(16), - RX_MPDU_RES_STATUS_CSUM_OK = BIT(17), - RX_MPDU_RES_STATUS_HASH_INDEX_MSK = (0x3F0000), - RX_MPDU_RES_STATUS_STA_ID_MSK = (0x1f000000), - RX_MPDU_RES_STATUS_RRF_KILL = BIT(29), - RX_MPDU_RES_STATUS_FILTERING_MSK = (0xc00000), - RX_MPDU_RES_STATUS2_FILTERING_MSK = (0xc0000000), -}; - /** * struct iwl_radio_version_notif - information on the radio version * ( RADIO_VERSION_NOTIFICATION = 0x68 ) -- cgit v0.10.2 From 183edd8484fd375d0f6d5e73b071c21b17243424 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 1 Sep 2015 14:16:00 +0300 Subject: iwlwifi: mvm: configure wowlan configuration only if connected Recent fw version added assert to make sure wowlan configuration is configured only when a station is connected. Change the driver behavior to pass this configuration only if we indeed have ap station id (i.e. connected). Signed-off-by: Eliad Peller Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 0092286..62eefab 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -1146,12 +1146,17 @@ int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) /* make sure we have no running tx while configuring the seqno */ synchronize_net(); - iwl_mvm_set_wowlan_data(mvm, &wowlan_config_cmd, &d0i3_iter_data); - ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, flags, - sizeof(wowlan_config_cmd), - &wowlan_config_cmd); - if (ret) - return ret; + /* configure wowlan configuration only if needed */ + if (mvm->d0i3_ap_sta_id != IWL_MVM_STATION_COUNT) { + iwl_mvm_set_wowlan_data(mvm, &wowlan_config_cmd, + &d0i3_iter_data); + + ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, flags, + sizeof(wowlan_config_cmd), + &wowlan_config_cmd); + if (ret) + return ret; + } return iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, flags | CMD_MAKE_TRANS_IDLE, -- cgit v0.10.2 From 7c014e35a018187462f2cd6c85259e080663ba2d Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 6 Sep 2015 14:17:17 +0300 Subject: iwlwifi: mvm: add debug print for d0i3 exit indication In order to verify d0i3 flow, add debug print to indicate d0i3 exit was completed (right after tx was re-enabled), along with the wakeup reasons. Signed-off-by: Eliad Peller Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 62eefab..3f6428c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -1263,7 +1263,7 @@ static void iwl_mvm_d0i3_exit_work(struct work_struct *wk) }; struct iwl_wowlan_status *status; int ret; - u32 handled_reasons, wakeup_reasons; + u32 handled_reasons, wakeup_reasons = 0; __le16 *qos_seq = NULL; mutex_lock(&mvm->mutex); @@ -1295,6 +1295,9 @@ static void iwl_mvm_d0i3_exit_work(struct work_struct *wk) out: iwl_mvm_d0i3_enable_tx(mvm, qos_seq); + IWL_DEBUG_INFO(mvm, "d0i3 exit completed (wakeup reasons: 0x%x)\n", + wakeup_reasons); + /* qos_seq might point inside resp_pkt, so free it only now */ if (get_status_cmd.resp_pkt) iwl_free_resp(&get_status_cmd); -- cgit v0.10.2 From 475b9eacc5543eea922431bb8ac4ec761de0e415 Mon Sep 17 00:00:00 2001 From: Nachiket Kukade Date: Fri, 18 Sep 2015 06:40:40 -0700 Subject: Bluetooth: btmrvl: fix firmware dump issue First firmware dump attempt from user works fine, but firmware goes into bad state after this. Subsequent attempts fails. As required by the firmware dump implementation, this change writes FW_DUMP_READ_DONE value to dump ctrl register to address this issue. Signed-off-by: Nachiket Kukade Signed-off-by: Amitkumar Karwar Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index b9978a7..5f7c9be 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -1278,6 +1278,12 @@ static void btmrvl_sdio_dump_firmware(struct btmrvl_private *priv) if (memory_size == 0) { BT_INFO("Firmware dump finished!"); + sdio_writeb(card->func, FW_DUMP_READ_DONE, + card->reg->fw_dump_ctrl, &ret); + if (ret) { + BT_ERR("SDIO Write MEMDUMP_FINISH ERR"); + goto done; + } break; } -- cgit v0.10.2 From cdd38b219eec2e1b83c0a02d89d372f9656648eb Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Fri, 18 Sep 2015 11:30:40 +0200 Subject: mac802154: llsec: fix device deletion from list This patch adds a missing list_del when a device description will be deleted. Cc: Phoebe Buckheister Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/mac802154/llsec.c b/net/mac802154/llsec.c index 985e939..7799d3c 100644 --- a/net/mac802154/llsec.c +++ b/net/mac802154/llsec.c @@ -401,6 +401,7 @@ int mac802154_llsec_dev_del(struct mac802154_llsec *sec, __le64 device_addr) hash_del_rcu(&pos->bucket_s); hash_del_rcu(&pos->bucket_hw); + list_del_rcu(&pos->dev.list); call_rcu(&pos->rcu, llsec_dev_free_rcu); return 0; -- cgit v0.10.2 From a1da67b8117ddbe88c770b48b5b1527393b8c9c0 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Fri, 18 Sep 2015 11:30:41 +0200 Subject: ieee802154: header_ops: fix frame control setting Sometimes upper-layer protocols wants to generate a new mac header by filling "struct ieee802154_hdr" only. These upper-layers sets for the address settings the source and dest fields, but not the fc fields for indicate the source and dest address mode. This patch changes the "ieee802154_hdr_push" function so the fc address fields are set according the source and dest fields of "struct ieee802154_hdr". Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h index 2c10a9f..95a71bc 100644 --- a/include/net/ieee802154_netdev.h +++ b/include/net/ieee802154_netdev.h @@ -99,7 +99,7 @@ struct ieee802154_hdr { * hdr->fc will be ignored. this includes the INTRA_PAN bit and the frame * version, if SECEN is set. */ -int ieee802154_hdr_push(struct sk_buff *skb, const struct ieee802154_hdr *hdr); +int ieee802154_hdr_push(struct sk_buff *skb, struct ieee802154_hdr *hdr); /* pulls the entire 802.15.4 header off of the skb, including the security * header, and performs pan id decompression diff --git a/net/ieee802154/header_ops.c b/net/ieee802154/header_ops.c index a051b69..d8443b0 100644 --- a/net/ieee802154/header_ops.c +++ b/net/ieee802154/header_ops.c @@ -83,35 +83,35 @@ ieee802154_hdr_push_sechdr(u8 *buf, const struct ieee802154_sechdr *hdr) } int -ieee802154_hdr_push(struct sk_buff *skb, const struct ieee802154_hdr *hdr) +ieee802154_hdr_push(struct sk_buff *skb, struct ieee802154_hdr *hdr) { u8 buf[MAC802154_FRAME_HARD_HEADER_LEN]; int pos = 2; int rc; - struct ieee802154_hdr_fc fc = hdr->fc; + struct ieee802154_hdr_fc *fc = &hdr->fc; buf[pos++] = hdr->seq; - fc.dest_addr_mode = hdr->dest.mode; + fc->dest_addr_mode = hdr->dest.mode; rc = ieee802154_hdr_push_addr(buf + pos, &hdr->dest, false); if (rc < 0) return -EINVAL; pos += rc; - fc.source_addr_mode = hdr->source.mode; + fc->source_addr_mode = hdr->source.mode; if (hdr->source.pan_id == hdr->dest.pan_id && hdr->dest.mode != IEEE802154_ADDR_NONE) - fc.intra_pan = true; + fc->intra_pan = true; - rc = ieee802154_hdr_push_addr(buf + pos, &hdr->source, fc.intra_pan); + rc = ieee802154_hdr_push_addr(buf + pos, &hdr->source, fc->intra_pan); if (rc < 0) return -EINVAL; pos += rc; - if (fc.security_enabled) { - fc.version = 1; + if (fc->security_enabled) { + fc->version = 1; rc = ieee802154_hdr_push_sechdr(buf + pos, &hdr->sec); if (rc < 0) @@ -120,7 +120,7 @@ ieee802154_hdr_push(struct sk_buff *skb, const struct ieee802154_hdr *hdr) pos += rc; } - memcpy(buf, &fc, 2); + memcpy(buf, fc, 2); memcpy(skb_push(skb, pos), buf, pos); -- cgit v0.10.2 From 838b83d63d2909f9136f3030dc4fffa8230c31da Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Fri, 18 Sep 2015 11:30:42 +0200 Subject: ieee802154: introduce wpan_dev_header_ops The current header_ops callback structure of net device are used mostly from 802.15.4 upper-layers. Because this callback structure is a very generic one, which is also used by e.g. DGRAM AF_PACKET sockets, we can't make this callback structure 802.15.4 specific which is currently is. I saw the smallest "constraint" for calling this callback with dev_hard_header/dev_parse_header by AF_PACKET which assign a 8 byte array for address void pointers. Currently 802.15.4 specific protocols like af802154 and 6LoWPAN will assign the "struct ieee802154_addr" as these parameters which is greater than 8 bytes. The current callback implementation for header_ops.create assumes always a complete "struct ieee802154_addr" which AF_PACKET can't never handled and is greater than 8 bytes. For that reason we introduce now a "generic" create/parse header_ops callback which allows handling with intra-pan extended addresses only. This allows a small use-case with AF_PACKET to send "somehow" a valid dataframe over DGRAM. To keeping the current dev_hard_header behaviour we introduce a similar callback structure "wpan_dev_header_ops" which contains 802.15.4 specific upper-layer header creation functionality, which can be called by wpan_dev_hard_header. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h index 76b1ffa..242273c 100644 --- a/include/net/cfg802154.h +++ b/include/net/cfg802154.h @@ -167,6 +167,26 @@ struct wpan_phy { char priv[0] __aligned(NETDEV_ALIGN); }; +struct ieee802154_addr { + u8 mode; + __le16 pan_id; + union { + __le16 short_addr; + __le64 extended_addr; + }; +}; + +struct wpan_dev_header_ops { + /* TODO create callback currently assumes ieee802154_mac_cb inside + * skb->cb. This should be changed to give these information as + * parameter. + */ + int (*create)(struct sk_buff *skb, struct net_device *dev, + const struct ieee802154_addr *daddr, + const struct ieee802154_addr *saddr, + unsigned int len); +}; + struct wpan_dev { struct wpan_phy *wpan_phy; int iftype; @@ -175,6 +195,8 @@ struct wpan_dev { struct list_head list; struct net_device *netdev; + const struct wpan_dev_header_ops *header_ops; + /* lowpan interface, set when the wpan_dev belongs to one lowpan_dev */ struct net_device *lowpan_dev; @@ -205,6 +227,17 @@ struct wpan_dev { #define to_phy(_dev) container_of(_dev, struct wpan_phy, dev) +static inline int +wpan_dev_hard_header(struct sk_buff *skb, struct net_device *dev, + const struct ieee802154_addr *daddr, + const struct ieee802154_addr *saddr, + unsigned int len) +{ + struct wpan_dev *wpan_dev = dev->ieee802154_ptr; + + return wpan_dev->header_ops->create(skb, dev, daddr, saddr, len); +} + struct wpan_phy * wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size); static inline void wpan_phy_set_dev(struct wpan_phy *phy, struct device *dev) diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h index 95a71bc..aebb9d8 100644 --- a/include/net/ieee802154_netdev.h +++ b/include/net/ieee802154_netdev.h @@ -50,15 +50,6 @@ struct ieee802154_sechdr { }; }; -struct ieee802154_addr { - u8 mode; - __le16 pan_id; - union { - __le16 short_addr; - __le64 extended_addr; - }; -}; - struct ieee802154_hdr_fc { #if defined(__LITTLE_ENDIAN_BITFIELD) u16 type:3, diff --git a/net/ieee802154/6lowpan/tx.c b/net/ieee802154/6lowpan/tx.c index 54939d0..6067e06 100644 --- a/net/ieee802154/6lowpan/tx.c +++ b/net/ieee802154/6lowpan/tx.c @@ -87,8 +87,8 @@ lowpan_alloc_frag(struct sk_buff *skb, int size, skb_reset_network_header(frag); *mac_cb(frag) = *mac_cb(skb); - rc = dev_hard_header(frag, wdev, 0, &master_hdr->dest, - &master_hdr->source, size); + rc = wpan_dev_hard_header(frag, wdev, &master_hdr->dest, + &master_hdr->source, size); if (rc < 0) { kfree_skb(frag); return ERR_PTR(rc); @@ -228,8 +228,8 @@ static int lowpan_header(struct sk_buff *skb, struct net_device *ldev, cb->ackreq = wpan_dev->ackreq; } - return dev_hard_header(skb, lowpan_dev_info(ldev)->wdev, ETH_P_IPV6, - (void *)&da, (void *)&sa, 0); + return wpan_dev_hard_header(skb, lowpan_dev_info(ldev)->wdev, &da, &sa, + 0); } netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *ldev) diff --git a/net/ieee802154/socket.c b/net/ieee802154/socket.c index b6eacf3..be77f21 100644 --- a/net/ieee802154/socket.c +++ b/net/ieee802154/socket.c @@ -676,8 +676,8 @@ static int dgram_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) cb->seclevel = ro->seclevel; cb->seclevel_override = ro->seclevel_override; - err = dev_hard_header(skb, dev, ETH_P_IEEE802154, &dst_addr, - ro->bound ? &ro->src_addr : NULL, size); + err = wpan_dev_hard_header(skb, dev, &dst_addr, + ro->bound ? &ro->src_addr : NULL, size); if (err < 0) goto out_skb; diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c index ed26952..8afe26d 100644 --- a/net/mac802154/iface.c +++ b/net/mac802154/iface.c @@ -367,12 +367,11 @@ static int mac802154_set_header_security(struct ieee802154_sub_if_data *sdata, return 0; } -static int mac802154_header_create(struct sk_buff *skb, - struct net_device *dev, - unsigned short type, - const void *daddr, - const void *saddr, - unsigned len) +static int ieee802154_header_create(struct sk_buff *skb, + struct net_device *dev, + const struct ieee802154_addr *daddr, + const struct ieee802154_addr *saddr, + unsigned len) { struct ieee802154_hdr hdr; struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); @@ -423,24 +422,91 @@ static int mac802154_header_create(struct sk_buff *skb, return hlen; } +static const struct wpan_dev_header_ops ieee802154_header_ops = { + .create = ieee802154_header_create, +}; + +/* This header create functionality assumes a 8 byte array for + * source and destination pointer at maximum. To adapt this for + * the 802.15.4 dataframe header we use extended address handling + * here only and intra pan connection. fc fields are mostly fallback + * handling. For provide dev_hard_header for dgram sockets. + */ +static int mac802154_header_create(struct sk_buff *skb, + struct net_device *dev, + unsigned short type, + const void *daddr, + const void *saddr, + unsigned len) +{ + struct ieee802154_hdr hdr; + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + struct wpan_dev *wpan_dev = &sdata->wpan_dev; + struct ieee802154_mac_cb cb = { }; + int hlen; + + if (!daddr) + return -EINVAL; + + memset(&hdr.fc, 0, sizeof(hdr.fc)); + hdr.fc.type = IEEE802154_FC_TYPE_DATA; + hdr.fc.ack_request = wpan_dev->ackreq; + hdr.seq = atomic_inc_return(&dev->ieee802154_ptr->dsn) & 0xFF; + + /* TODO currently a workaround to give zero cb block to set + * security parameters defaults according MIB. + */ + if (mac802154_set_header_security(sdata, &hdr, &cb) < 0) + return -EINVAL; + + hdr.dest.pan_id = wpan_dev->pan_id; + hdr.dest.mode = IEEE802154_ADDR_LONG; + memcpy(&hdr.dest.extended_addr, daddr, IEEE802154_EXTENDED_ADDR_LEN); + + hdr.source.pan_id = hdr.dest.pan_id; + hdr.source.mode = IEEE802154_ADDR_LONG; + + if (!saddr) + hdr.source.extended_addr = wpan_dev->extended_addr; + else + memcpy(&hdr.source.extended_addr, saddr, + IEEE802154_EXTENDED_ADDR_LEN); + + hlen = ieee802154_hdr_push(skb, &hdr); + if (hlen < 0) + return -EINVAL; + + skb_reset_mac_header(skb); + skb->mac_len = hlen; + + if (len > ieee802154_max_payload(&hdr)) + return -EMSGSIZE; + + return hlen; +} + static int mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr) { struct ieee802154_hdr hdr; - struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr; if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) { pr_debug("malformed packet\n"); return 0; } - *addr = hdr.source; - return sizeof(*addr); + if (hdr.source.mode == IEEE802154_ADDR_LONG) { + memcpy(haddr, &hdr.source.extended_addr, + IEEE802154_EXTENDED_ADDR_LEN); + return IEEE802154_EXTENDED_ADDR_LEN; + } + + return 0; } -static struct header_ops mac802154_header_ops = { - .create = mac802154_header_create, - .parse = mac802154_header_parse, +static const struct header_ops mac802154_header_ops = { + .create = mac802154_header_create, + .parse = mac802154_header_parse, }; static const struct net_device_ops mac802154_wpan_ops = { @@ -513,6 +579,7 @@ ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata, sdata->dev->netdev_ops = &mac802154_wpan_ops; sdata->dev->ml_priv = &mac802154_mlme_wpan; wpan_dev->promiscuous_mode = false; + wpan_dev->header_ops = &ieee802154_header_ops; mutex_init(&sdata->sec_mtx); -- cgit v0.10.2 From 87a93e4eceb495f93e3f37b100334d2641765b6c Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Fri, 18 Sep 2015 11:30:43 +0200 Subject: ieee802154: change needed headroom/tailroom This patch cleanups needed_headroom, needed_tailroom and hard_header_len fields for wpan and lowpan interfaces. For wpan interfaces the worst case mac header len should be part of needed_headroom, currently this is set as hard_header_len, but hard_header_len should be set to the minimum header length which xmit call assumes and this is the minimum frame length of 802.15.4. The hard_header_len value will check inside send callbacl of AF_PACKET raw sockets. For lowpan interfaces, if fragmentation isn't needed the skb will call dev_hard_header for 802154 layer and queue it afterwards. This happens without new skb allocation, so we need the same headroom and tailroom lengths like 802154 inside 802154 6lowpan layer. At least we assume as minimum header length an ipv6 header size. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/include/linux/ieee802154.h b/include/linux/ieee802154.h index db01492..205ce4e 100644 --- a/include/linux/ieee802154.h +++ b/include/linux/ieee802154.h @@ -31,6 +31,17 @@ #define IEEE802154_ACK_PSDU_LEN 5 #define IEEE802154_MIN_PSDU_LEN 9 #define IEEE802154_FCS_LEN 2 +#define IEEE802154_MAX_AUTH_TAG_LEN 16 + +/* General MAC frame format: + * 2 bytes: Frame Control + * 1 byte: Sequence Number + * 20 bytes: Addressing fields + * 14 bytes: Auxiliary Security Header + */ +#define IEEE802154_MAX_HEADER_LEN (2 + 1 + 20 + 14) +#define IEEE802154_MIN_HEADER_LEN (IEEE802154_ACK_PSDU_LEN - \ + IEEE802154_FCS_LEN) #define IEEE802154_PAN_ID_BROADCAST 0xffff #define IEEE802154_ADDR_SHORT_BROADCAST 0xffff diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h index eeae5eb..c17f556 100644 --- a/include/net/6lowpan.h +++ b/include/net/6lowpan.h @@ -61,6 +61,14 @@ #define UIP_PROTO_UDP 17 /* ipv6 next header value for UDP */ #define UIP_FRAGH_LEN 8 /* ipv6 fragment header size */ +#define LOWPAN_NHC_MAX_ID_LEN 1 +/* Max IPHC Header len without IPv6 hdr specific inline data. + * Useful for getting the "extra" bytes we need at worst case compression. + * + * LOWPAN_IPHC + CID + LOWPAN_NHC_MAX_ID_LEN + */ +#define LOWPAN_IPHC_MAX_HEADER_LEN (2 + 1 + LOWPAN_NHC_MAX_ID_LEN) + /* * ipv6 address based on mac * second bit-flip (Universe/Local) is done according RFC2464 diff --git a/include/net/mac802154.h b/include/net/mac802154.h index 32bd7c0..2c47850 100644 --- a/include/net/mac802154.h +++ b/include/net/mac802154.h @@ -23,14 +23,6 @@ #include -/* General MAC frame format: - * 2 bytes: Frame Control - * 1 byte: Sequence Number - * 20 bytes: Addressing fields - * 14 bytes: Auxiliary Security Header - */ -#define MAC802154_FRAME_HARD_HEADER_LEN (2 + 1 + 20 + 14) - /** * enum ieee802154_hw_addr_filt_flags - hardware address filtering flags * diff --git a/net/6lowpan/nhc.h b/net/6lowpan/nhc.h index ed44938..c249f17 100644 --- a/net/6lowpan/nhc.h +++ b/net/6lowpan/nhc.h @@ -8,8 +8,6 @@ #include #include -#define LOWPAN_NHC_MAX_ID_LEN 1 - /** * LOWPAN_NHC - helper macro to generate nh id fields and lowpan_nhc struct * diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c index 9f0cfa5..44420ed 100644 --- a/net/ieee802154/6lowpan/core.c +++ b/net/ieee802154/6lowpan/core.c @@ -104,9 +104,8 @@ static void lowpan_setup(struct net_device *ldev) ldev->addr_len = IEEE802154_ADDR_LEN; memset(ldev->broadcast, 0xff, IEEE802154_ADDR_LEN); ldev->type = ARPHRD_6LOWPAN; - /* Frame Control + Sequence Number + Address fields + Security Header */ - ldev->hard_header_len = 2 + 1 + 20 + 14; - ldev->needed_tailroom = 2; /* FCS */ + /* We need an ipv6hdr as minimum len when calling xmit */ + ldev->hard_header_len = sizeof(struct ipv6hdr); ldev->mtu = IPV6_MIN_MTU; ldev->priv_flags |= IFF_NO_QUEUE; ldev->flags = IFF_BROADCAST | IFF_MULTICAST; @@ -156,6 +155,15 @@ static int lowpan_newlink(struct net *src_net, struct net_device *ldev, lowpan_dev_info(ldev)->wdev = wdev; /* Set the lowpan hardware address to the wpan hardware address. */ memcpy(ldev->dev_addr, wdev->dev_addr, IEEE802154_ADDR_LEN); + /* We need headroom for possible wpan_dev_hard_header call and tailroom + * for encryption/fcs handling. The lowpan interface will replace + * the IPv6 header with 6LoWPAN header. At worst case the 6LoWPAN + * header has LOWPAN_IPHC_MAX_HEADER_LEN more bytes than the IPv6 + * header. + */ + ldev->needed_headroom = LOWPAN_IPHC_MAX_HEADER_LEN + + wdev->needed_headroom; + ldev->needed_tailroom = wdev->needed_tailroom; lowpan_netdev_setup(ldev, LOWPAN_LLTYPE_IEEE802154); diff --git a/net/ieee802154/6lowpan/tx.c b/net/ieee802154/6lowpan/tx.c index 6067e06..7e0563e 100644 --- a/net/ieee802154/6lowpan/tx.c +++ b/net/ieee802154/6lowpan/tx.c @@ -10,6 +10,7 @@ #include #include +#include #include "6lowpan_i.h" @@ -36,6 +37,13 @@ lowpan_addr_info *lowpan_skb_priv(const struct sk_buff *skb) sizeof(struct lowpan_addr_info)); } +/* This callback will be called from AF_PACKET and IPv6 stack, the AF_PACKET + * sockets gives an 8 byte array for addresses only! + * + * TODO I think AF_PACKET DGRAM (sending/receiving) RAW (sending) makes no + * sense here. We should disable it, the right use-case would be AF_INET6 + * RAW/DGRAM sockets. + */ int lowpan_header_create(struct sk_buff *skb, struct net_device *ldev, unsigned short type, const void *_daddr, const void *_saddr, unsigned int len) @@ -77,13 +85,13 @@ lowpan_alloc_frag(struct sk_buff *skb, int size, struct sk_buff *frag; int rc; - frag = alloc_skb(wdev->hard_header_len + wdev->needed_tailroom + size, + frag = alloc_skb(wdev->needed_headroom + wdev->needed_tailroom + size, GFP_ATOMIC); if (likely(frag)) { frag->dev = wdev; frag->priority = skb->priority; - skb_reserve(frag, wdev->hard_header_len); + skb_reserve(frag, wdev->needed_headroom); skb_reset_network_header(frag); *mac_cb(frag) = *mac_cb(skb); diff --git a/net/ieee802154/header_ops.c b/net/ieee802154/header_ops.c index d8443b0..c7439f0 100644 --- a/net/ieee802154/header_ops.c +++ b/net/ieee802154/header_ops.c @@ -85,7 +85,7 @@ ieee802154_hdr_push_sechdr(u8 *buf, const struct ieee802154_sechdr *hdr) int ieee802154_hdr_push(struct sk_buff *skb, struct ieee802154_hdr *hdr) { - u8 buf[MAC802154_FRAME_HARD_HEADER_LEN]; + u8 buf[IEEE802154_MAX_HEADER_LEN]; int pos = 2; int rc; struct ieee802154_hdr_fc *fc = &hdr->fc; diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c index 8afe26d..b5a0936 100644 --- a/net/mac802154/iface.c +++ b/net/mac802154/iface.c @@ -537,8 +537,18 @@ static void ieee802154_if_setup(struct net_device *dev) dev->addr_len = IEEE802154_EXTENDED_ADDR_LEN; memset(dev->broadcast, 0xff, IEEE802154_EXTENDED_ADDR_LEN); - dev->hard_header_len = MAC802154_FRAME_HARD_HEADER_LEN; - dev->needed_tailroom = 2 + 16; /* FCS + MIC */ + /* Let hard_header_len set to IEEE802154_MIN_HEADER_LEN. AF_PACKET + * will not send frames without any payload, but ack frames + * has no payload, so substract one that we can send a 3 bytes + * frame. The xmit callback assumes at least a hard header where two + * bytes fc and sequence field are set. + */ + dev->hard_header_len = IEEE802154_MIN_HEADER_LEN - 1; + /* The auth_tag header is for security and places in private payload + * room of mac frame which stucks between payload and FCS field. + */ + dev->needed_tailroom = IEEE802154_MAX_AUTH_TAG_LEN + + IEEE802154_FCS_LEN; dev->mtu = IEEE802154_MTU; dev->tx_queue_len = 300; dev->flags = IFF_NOARP | IFF_BROADCAST; @@ -617,7 +627,8 @@ ieee802154_if_add(struct ieee802154_local *local, const char *name, if (!ndev) return ERR_PTR(-ENOMEM); - ndev->needed_headroom = local->hw.extra_tx_headroom; + ndev->needed_headroom = local->hw.extra_tx_headroom + + IEEE802154_MAX_HEADER_LEN; ret = dev_alloc_name(ndev, ndev->name); if (ret < 0) diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c index 7ed4391..66d7ecb 100644 --- a/net/mac802154/tx.c +++ b/net/mac802154/tx.c @@ -77,9 +77,6 @@ ieee802154_tx(struct ieee802154_local *local, struct sk_buff *skb) put_unaligned_le16(crc, skb_put(skb, 2)); } - if (skb_cow_head(skb, local->hw.extra_tx_headroom)) - goto err_tx; - /* Stop the netif queue on each sub_if_data object. */ ieee802154_stop_queue(&local->hw); -- cgit v0.10.2 From 02c7b6922899621aa8e8babe27fca7b6b2e497b0 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Fri, 18 Sep 2015 11:30:44 +0200 Subject: mac802154: tx: add warning if MTU exceeds Sending over AF_PACKET RAW sockets we can sending frames which exceeds MTU size. To handling it correct we need to change things in AF_PACKET which knows on RAW sockets an additional FCS is set by hardware or mac802154 transmit functionality. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c index 66d7ecb..5ee596e 100644 --- a/net/mac802154/tx.c +++ b/net/mac802154/tx.c @@ -71,6 +71,17 @@ ieee802154_tx(struct ieee802154_local *local, struct sk_buff *skb) struct net_device *dev = skb->dev; int ret; + /* This check is for AF_PACKET RAW socket only, which doesn't + * know about the FCS which is set here or by hardware. otherwise + * it should not occur in any case! + * + * TODO: This should be handled in AF_PACKET and return -EMSGSIZE. + */ + if (skb->len > IEEE802154_MTU - IEEE802154_FCS_LEN) { + netdev_warn(dev, "Frame len above MTU limit. Dropped.\n"); + goto err_tx; + } + if (!(local->hw.flags & IEEE802154_HW_TX_OMIT_CKSUM)) { u16 crc = crc_ccitt(0, skb->data, skb->len); -- cgit v0.10.2 From 57c1bc7ea8a4857070722c23ce98e01f256c9306 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 21 Sep 2015 09:37:54 +0200 Subject: at86rf230: support edge triggered irq This patch adds support for edge triggered irq types. We remove the locking for irq resources by enable/disable irq and allocate directly some heap buffer at isr. We have still a enable/disable irq path but this is for level-triggered irq's which need to be disabled until spi_async clear the irq line. There is usually a little race condition between "irq line cleared" and "enable_irq". When in this time a edge triggered irq arrived, we will not recognize this interrupt. This case can't happend at at86rf230. The reason is that we unmask TRX_END irq's only which indicates a transmit or receive completion, which depends on the current state. On Transmit: TRX_END arrived and transceiver is in TX_ARET_ON state again, in this state no other TRX_END can happen until we leave the state. On Receive: This is protected with the RX_SAFE_MODE bit which leaves the transceiver in RX_AACK_BUSY until we readed the framebuffer. In this state no other TRX_END can happen. Tested with RPi where I first detected issues between edge/level irq's. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 9756e64..de6e4fa 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -81,7 +81,7 @@ struct at86rf230_state_change { u8 from_state; u8 to_state; - bool irq_enable; + bool free; }; struct at86rf230_trac { @@ -105,8 +105,6 @@ struct at86rf230_local { struct completion state_complete; struct at86rf230_state_change state; - struct at86rf230_state_change irq; - unsigned long cal_timeout; bool is_tx; bool is_tx_from_off; @@ -122,8 +120,7 @@ struct at86rf230_local { static void at86rf230_async_state_change(struct at86rf230_local *lp, struct at86rf230_state_change *ctx, - const u8 state, void (*complete)(void *context), - const bool irq_enable); + const u8 state, void (*complete)(void *context)); static inline void at86rf230_sleep(struct at86rf230_local *lp) @@ -352,8 +349,10 @@ at86rf230_async_error_recover(void *context) struct at86rf230_local *lp = ctx->lp; lp->is_tx = 0; - at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, NULL, false); + at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, NULL); ieee802154_wake_queue(lp->hw); + if (ctx->free) + kfree(ctx); } static inline void @@ -363,15 +362,14 @@ at86rf230_async_error(struct at86rf230_local *lp, dev_err(&lp->spi->dev, "spi_async error %d\n", rc); at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF, - at86rf230_async_error_recover, false); + at86rf230_async_error_recover); } /* Generic function to get some register value in async mode */ static void -at86rf230_async_read_reg(struct at86rf230_local *lp, const u8 reg, +at86rf230_async_read_reg(struct at86rf230_local *lp, u8 reg, struct at86rf230_state_change *ctx, - void (*complete)(void *context), - const bool irq_enable) + void (*complete)(void *context)) { int rc; @@ -379,14 +377,24 @@ at86rf230_async_read_reg(struct at86rf230_local *lp, const u8 reg, tx_buf[0] = (reg & CMD_REG_MASK) | CMD_REG; ctx->msg.complete = complete; - ctx->irq_enable = irq_enable; rc = spi_async(lp->spi, &ctx->msg); - if (rc) { - if (irq_enable) - enable_irq(ctx->irq); + if (rc) + at86rf230_async_error(lp, ctx, rc); +} +static void +at86rf230_async_write_reg(struct at86rf230_local *lp, u8 reg, u8 val, + struct at86rf230_state_change *ctx, + void (*complete)(void *context)) +{ + int rc; + + ctx->buf[0] = (reg & CMD_REG_MASK) | CMD_REG | CMD_WRITE; + ctx->buf[1] = val; + ctx->msg.complete = complete; + rc = spi_async(lp->spi, &ctx->msg); + if (rc) at86rf230_async_error(lp, ctx, rc); - } } static void @@ -434,8 +442,7 @@ at86rf230_async_state_assert(void *context) lp->tx_retry++; at86rf230_async_state_change(lp, ctx, state, - ctx->complete, - ctx->irq_enable); + ctx->complete); return; } } @@ -456,8 +463,7 @@ static enum hrtimer_restart at86rf230_async_state_timer(struct hrtimer *timer) struct at86rf230_local *lp = ctx->lp; at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx, - at86rf230_async_state_assert, - ctx->irq_enable); + at86rf230_async_state_assert); return HRTIMER_NORESTART; } @@ -562,14 +568,12 @@ at86rf230_async_state_change_start(void *context) struct at86rf230_local *lp = ctx->lp; u8 *buf = ctx->buf; const u8 trx_state = buf[1] & TRX_STATE_MASK; - int rc; /* Check for "possible" STATE_TRANSITION_IN_PROGRESS */ if (trx_state == STATE_TRANSITION_IN_PROGRESS) { udelay(1); at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx, - at86rf230_async_state_change_start, - ctx->irq_enable); + at86rf230_async_state_change_start); return; } @@ -586,31 +590,20 @@ at86rf230_async_state_change_start(void *context) /* Going into the next step for a state change which do a timing * relevant delay. */ - buf[0] = (RG_TRX_STATE & CMD_REG_MASK) | CMD_REG | CMD_WRITE; - buf[1] = ctx->to_state; - ctx->msg.complete = at86rf230_async_state_delay; - rc = spi_async(lp->spi, &ctx->msg); - if (rc) { - if (ctx->irq_enable) - enable_irq(ctx->irq); - - at86rf230_async_error(lp, ctx, rc); - } + at86rf230_async_write_reg(lp, RG_TRX_STATE, ctx->to_state, ctx, + at86rf230_async_state_delay); } static void at86rf230_async_state_change(struct at86rf230_local *lp, struct at86rf230_state_change *ctx, - const u8 state, void (*complete)(void *context), - const bool irq_enable) + const u8 state, void (*complete)(void *context)) { /* Initialization for the state change context */ ctx->to_state = state; ctx->complete = complete; - ctx->irq_enable = irq_enable; at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx, - at86rf230_async_state_change_start, - irq_enable); + at86rf230_async_state_change_start); } static void @@ -632,8 +625,7 @@ at86rf230_sync_state_change(struct at86rf230_local *lp, unsigned int state) unsigned long rc; at86rf230_async_state_change(lp, &lp->state, state, - at86rf230_sync_state_change_complete, - false); + at86rf230_sync_state_change_complete); rc = wait_for_completion_timeout(&lp->state_complete, msecs_to_jiffies(100)); @@ -651,9 +643,8 @@ at86rf230_tx_complete(void *context) struct at86rf230_state_change *ctx = context; struct at86rf230_local *lp = ctx->lp; - enable_irq(ctx->irq); - ieee802154_xmit_complete(lp->hw, lp->tx_skb, false); + kfree(ctx); } static void @@ -663,7 +654,7 @@ at86rf230_tx_on(void *context) struct at86rf230_local *lp = ctx->lp; at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, - at86rf230_tx_complete, true); + at86rf230_tx_complete); } static void @@ -697,8 +688,7 @@ at86rf230_tx_trac_check(void *context) } } - at86rf230_async_state_change(lp, &lp->irq, STATE_TX_ON, - at86rf230_tx_on, true); + at86rf230_async_state_change(lp, ctx, STATE_TX_ON, at86rf230_tx_on); } static void @@ -706,7 +696,6 @@ at86rf230_rx_read_frame_complete(void *context) { struct at86rf230_state_change *ctx = context; struct at86rf230_local *lp = ctx->lp; - u8 rx_local_buf[AT86RF2XX_MAX_BUF]; const u8 *buf = ctx->buf; struct sk_buff *skb; u8 len, lqi; @@ -718,18 +707,16 @@ at86rf230_rx_read_frame_complete(void *context) } lqi = buf[2 + len]; - memcpy(rx_local_buf, buf + 2, len); - ctx->trx.len = 2; - enable_irq(ctx->irq); - skb = dev_alloc_skb(IEEE802154_MTU); if (!skb) { dev_vdbg(&lp->spi->dev, "failed to allocate sk_buff\n"); + kfree(ctx); return; } - memcpy(skb_put(skb, len), rx_local_buf, len); + memcpy(skb_put(skb, len), buf + 2, len); ieee802154_rx_irqsafe(lp->hw, skb, lqi); + kfree(ctx); } static void @@ -765,21 +752,23 @@ at86rf230_rx_trac_check(void *context) rc = spi_async(lp->spi, &ctx->msg); if (rc) { ctx->trx.len = 2; - enable_irq(ctx->irq); at86rf230_async_error(lp, ctx, rc); } } static void -at86rf230_irq_trx_end(struct at86rf230_local *lp) +at86rf230_irq_trx_end(void *context) { + struct at86rf230_state_change *ctx = context; + struct at86rf230_local *lp = ctx->lp; + if (lp->is_tx) { lp->is_tx = 0; - at86rf230_async_read_reg(lp, RG_TRX_STATE, &lp->irq, - at86rf230_tx_trac_check, true); + at86rf230_async_read_reg(lp, RG_TRX_STATE, ctx, + at86rf230_tx_trac_check); } else { - at86rf230_async_read_reg(lp, RG_TRX_STATE, &lp->irq, - at86rf230_rx_trac_check, true); + at86rf230_async_read_reg(lp, RG_TRX_STATE, ctx, + at86rf230_rx_trac_check); } } @@ -789,32 +778,59 @@ at86rf230_irq_status(void *context) struct at86rf230_state_change *ctx = context; struct at86rf230_local *lp = ctx->lp; const u8 *buf = ctx->buf; - const u8 irq = buf[1]; + u8 irq = buf[1]; + + enable_irq(lp->spi->irq); if (irq & IRQ_TRX_END) { - at86rf230_irq_trx_end(lp); + at86rf230_irq_trx_end(ctx); } else { - enable_irq(ctx->irq); dev_err(&lp->spi->dev, "not supported irq %02x received\n", irq); + kfree(ctx); } } +static void +at86rf230_setup_spi_messages(struct at86rf230_local *lp, + struct at86rf230_state_change *state) +{ + state->lp = lp; + state->irq = lp->spi->irq; + spi_message_init(&state->msg); + state->msg.context = state; + state->trx.len = 2; + state->trx.tx_buf = state->buf; + state->trx.rx_buf = state->buf; + spi_message_add_tail(&state->trx, &state->msg); + hrtimer_init(&state->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + state->timer.function = at86rf230_async_state_timer; +} + static irqreturn_t at86rf230_isr(int irq, void *data) { struct at86rf230_local *lp = data; - struct at86rf230_state_change *ctx = &lp->irq; - u8 *buf = ctx->buf; + struct at86rf230_state_change *ctx; int rc; disable_irq_nosync(irq); - buf[0] = (RG_IRQ_STATUS & CMD_REG_MASK) | CMD_REG; + ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC); + if (!ctx) { + enable_irq(irq); + return IRQ_NONE; + } + + at86rf230_setup_spi_messages(lp, ctx); + /* tell on error handling to free ctx */ + ctx->free = true; + + ctx->buf[0] = (RG_IRQ_STATUS & CMD_REG_MASK) | CMD_REG; ctx->msg.complete = at86rf230_irq_status; rc = spi_async(lp->spi, &ctx->msg); if (rc) { - enable_irq(irq); at86rf230_async_error(lp, ctx, rc); + enable_irq(irq); return IRQ_NONE; } @@ -826,21 +842,14 @@ at86rf230_write_frame_complete(void *context) { struct at86rf230_state_change *ctx = context; struct at86rf230_local *lp = ctx->lp; - u8 *buf = ctx->buf; - int rc; ctx->trx.len = 2; - if (gpio_is_valid(lp->slp_tr)) { + if (gpio_is_valid(lp->slp_tr)) at86rf230_slp_tr_rising_edge(lp); - } else { - buf[0] = (RG_TRX_STATE & CMD_REG_MASK) | CMD_REG | CMD_WRITE; - buf[1] = STATE_BUSY_TX; - ctx->msg.complete = NULL; - rc = spi_async(lp->spi, &ctx->msg); - if (rc) - at86rf230_async_error(lp, ctx, rc); - } + else + at86rf230_async_write_reg(lp, RG_TRX_STATE, STATE_BUSY_TX, ctx, + NULL); } static void @@ -873,7 +882,7 @@ at86rf230_xmit_tx_on(void *context) struct at86rf230_local *lp = ctx->lp; at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON, - at86rf230_write_frame, false); + at86rf230_write_frame); } static void @@ -886,12 +895,10 @@ at86rf230_xmit_start(void *context) if (lp->is_tx_from_off) { lp->is_tx_from_off = false; at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON, - at86rf230_write_frame, - false); + at86rf230_write_frame); } else { at86rf230_async_state_change(lp, ctx, STATE_TX_ON, - at86rf230_xmit_tx_on, - false); + at86rf230_xmit_tx_on); } } @@ -914,7 +921,7 @@ at86rf230_xmit(struct ieee802154_hw *hw, struct sk_buff *skb) if (time_is_before_jiffies(lp->cal_timeout)) { lp->is_tx_from_off = true; at86rf230_async_state_change(lp, ctx, STATE_TRX_OFF, - at86rf230_xmit_start, false); + at86rf230_xmit_start); } else { at86rf230_xmit_start(ctx); } @@ -1373,10 +1380,6 @@ static int at86rf230_hw_init(struct at86rf230_local *lp, u8 xtal_trim) return rc; irq_type = irq_get_trigger_type(lp->spi->irq); - if (irq_type == IRQ_TYPE_EDGE_RISING || - irq_type == IRQ_TYPE_EDGE_FALLING) - dev_warn(&lp->spi->dev, - "Using edge triggered irq's are not recommended, because it can cause races and result in a non-functional driver!\n"); if (irq_type == IRQ_TYPE_EDGE_FALLING || irq_type == IRQ_TYPE_LEVEL_LOW) irq_pol = IRQ_ACTIVE_LOW; @@ -1602,43 +1605,6 @@ not_supp: return rc; } -static void -at86rf230_setup_spi_messages(struct at86rf230_local *lp) -{ - lp->state.lp = lp; - lp->state.irq = lp->spi->irq; - spi_message_init(&lp->state.msg); - lp->state.msg.context = &lp->state; - lp->state.trx.len = 2; - lp->state.trx.tx_buf = lp->state.buf; - lp->state.trx.rx_buf = lp->state.buf; - spi_message_add_tail(&lp->state.trx, &lp->state.msg); - hrtimer_init(&lp->state.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - lp->state.timer.function = at86rf230_async_state_timer; - - lp->irq.lp = lp; - lp->irq.irq = lp->spi->irq; - spi_message_init(&lp->irq.msg); - lp->irq.msg.context = &lp->irq; - lp->irq.trx.len = 2; - lp->irq.trx.tx_buf = lp->irq.buf; - lp->irq.trx.rx_buf = lp->irq.buf; - spi_message_add_tail(&lp->irq.trx, &lp->irq.msg); - hrtimer_init(&lp->irq.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - lp->irq.timer.function = at86rf230_async_state_timer; - - lp->tx.lp = lp; - lp->tx.irq = lp->spi->irq; - spi_message_init(&lp->tx.msg); - lp->tx.msg.context = &lp->tx; - lp->tx.trx.len = 2; - lp->tx.trx.tx_buf = lp->tx.buf; - lp->tx.trx.rx_buf = lp->tx.buf; - spi_message_add_tail(&lp->tx.trx, &lp->tx.msg); - hrtimer_init(&lp->tx.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - lp->tx.timer.function = at86rf230_async_state_timer; -} - #ifdef CONFIG_IEEE802154_AT86RF230_DEBUGFS static struct dentry *at86rf230_debugfs_root; @@ -1760,7 +1726,8 @@ static int at86rf230_probe(struct spi_device *spi) goto free_dev; } - at86rf230_setup_spi_messages(lp); + at86rf230_setup_spi_messages(lp, &lp->state); + at86rf230_setup_spi_messages(lp, &lp->tx); rc = at86rf230_detect_device(lp); if (rc < 0) -- cgit v0.10.2 From c9f883f6fe66ff08ea968494709f4c0b28bc619a Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 21 Sep 2015 11:24:22 +0200 Subject: mrf24j40: cleanup define identation This patch replaces the spaces after define by a tab. Reviewed-by: Stefan Schmidt Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index 997724b..2b7fc00 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -23,46 +23,46 @@ #include /* MRF24J40 Short Address Registers */ -#define REG_RXMCR 0x00 /* Receive MAC control */ -#define REG_PANIDL 0x01 /* PAN ID (low) */ -#define REG_PANIDH 0x02 /* PAN ID (high) */ -#define REG_SADRL 0x03 /* Short address (low) */ -#define REG_SADRH 0x04 /* Short address (high) */ -#define REG_EADR0 0x05 /* Long address (low) (high is EADR7) */ -#define REG_TXMCR 0x11 /* Transmit MAC control */ -#define REG_PACON0 0x16 /* Power Amplifier Control */ -#define REG_PACON1 0x17 /* Power Amplifier Control */ -#define REG_PACON2 0x18 /* Power Amplifier Control */ -#define REG_TXNCON 0x1B /* Transmit Normal FIFO Control */ -#define REG_TXSTAT 0x24 /* TX MAC Status Register */ -#define REG_SOFTRST 0x2A /* Soft Reset */ -#define REG_TXSTBL 0x2E /* TX Stabilization */ -#define REG_INTSTAT 0x31 /* Interrupt Status */ -#define REG_INTCON 0x32 /* Interrupt Control */ -#define REG_GPIO 0x33 /* GPIO */ -#define REG_TRISGPIO 0x34 /* GPIO direction */ -#define REG_RFCTL 0x36 /* RF Control Mode Register */ -#define REG_BBREG1 0x39 /* Baseband Registers */ -#define REG_BBREG2 0x3A /* */ -#define REG_BBREG6 0x3E /* */ -#define REG_CCAEDTH 0x3F /* Energy Detection Threshold */ +#define REG_RXMCR 0x00 /* Receive MAC control */ +#define REG_PANIDL 0x01 /* PAN ID (low) */ +#define REG_PANIDH 0x02 /* PAN ID (high) */ +#define REG_SADRL 0x03 /* Short address (low) */ +#define REG_SADRH 0x04 /* Short address (high) */ +#define REG_EADR0 0x05 /* Long address (low) (high is EADR7) */ +#define REG_TXMCR 0x11 /* Transmit MAC control */ +#define REG_PACON0 0x16 /* Power Amplifier Control */ +#define REG_PACON1 0x17 /* Power Amplifier Control */ +#define REG_PACON2 0x18 /* Power Amplifier Control */ +#define REG_TXNCON 0x1B /* Transmit Normal FIFO Control */ +#define REG_TXSTAT 0x24 /* TX MAC Status Register */ +#define REG_SOFTRST 0x2A /* Soft Reset */ +#define REG_TXSTBL 0x2E /* TX Stabilization */ +#define REG_INTSTAT 0x31 /* Interrupt Status */ +#define REG_INTCON 0x32 /* Interrupt Control */ +#define REG_GPIO 0x33 /* GPIO */ +#define REG_TRISGPIO 0x34 /* GPIO direction */ +#define REG_RFCTL 0x36 /* RF Control Mode Register */ +#define REG_BBREG1 0x39 /* Baseband Registers */ +#define REG_BBREG2 0x3A /* */ +#define REG_BBREG6 0x3E /* */ +#define REG_CCAEDTH 0x3F /* Energy Detection Threshold */ /* MRF24J40 Long Address Registers */ -#define REG_RFCON0 0x200 /* RF Control Registers */ -#define REG_RFCON1 0x201 -#define REG_RFCON2 0x202 -#define REG_RFCON3 0x203 -#define REG_RFCON5 0x205 -#define REG_RFCON6 0x206 -#define REG_RFCON7 0x207 -#define REG_RFCON8 0x208 -#define REG_RSSI 0x210 -#define REG_SLPCON0 0x211 /* Sleep Clock Control Registers */ -#define REG_SLPCON1 0x220 -#define REG_WAKETIMEL 0x222 /* Wake-up Time Match Value Low */ -#define REG_WAKETIMEH 0x223 /* Wake-up Time Match Value High */ -#define REG_TESTMODE 0x22F /* Test mode */ -#define REG_RX_FIFO 0x300 /* Receive FIFO */ +#define REG_RFCON0 0x200 /* RF Control Registers */ +#define REG_RFCON1 0x201 +#define REG_RFCON2 0x202 +#define REG_RFCON3 0x203 +#define REG_RFCON5 0x205 +#define REG_RFCON6 0x206 +#define REG_RFCON7 0x207 +#define REG_RFCON8 0x208 +#define REG_RSSI 0x210 +#define REG_SLPCON0 0x211 /* Sleep Clock Control Registers */ +#define REG_SLPCON1 0x220 +#define REG_WAKETIMEL 0x222 /* Wake-up Time Match Value Low */ +#define REG_WAKETIMEH 0x223 /* Wake-up Time Match Value High */ +#define REG_TESTMODE 0x22F /* Test mode */ +#define REG_RX_FIFO 0x300 /* Receive FIFO */ /* Device configuration: Only channels 11-26 on page 0 are supported. */ #define MRF24J40_CHAN_MIN 11 -- cgit v0.10.2 From b2cfdf3c6720b9b77758ef229401f28f48c3372b Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 21 Sep 2015 11:24:23 +0200 Subject: mrf24j40: use ieee802154_alloc_hw for private data This patch removes the own private dataroom allocation by calling devm_kzalloc for devrec and assign this pointer to "devrec->hw->priv". Instead we using like all other drivers ieee802154_alloc_hw and give the size for the private driver dataroom at the first argument. Reviewed-by: Stefan Schmidt Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index 2b7fc00..1023cd2 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -723,16 +723,28 @@ err_ret: static int mrf24j40_probe(struct spi_device *spi) { int ret = -ENOMEM; + struct ieee802154_hw *hw; struct mrf24j40 *devrec; dev_info(&spi->dev, "probe(). IRQ: %d\n", spi->irq); - devrec = devm_kzalloc(&spi->dev, sizeof(struct mrf24j40), GFP_KERNEL); - if (!devrec) + /* Register with the 802154 subsystem */ + + hw = ieee802154_alloc_hw(sizeof(*devrec), &mrf24j40_ops); + if (!hw) goto err_ret; + + devrec = hw->priv; + devrec->spi = spi; + spi_set_drvdata(spi, devrec); + devrec->hw = hw; + devrec->hw->parent = &spi->dev; + devrec->hw->phy->supported.channels[0] = CHANNEL_MASK; + devrec->hw->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AFILT; + devrec->buf = devm_kzalloc(&spi->dev, 3, GFP_KERNEL); if (!devrec->buf) - goto err_ret; + goto err_register_device; spi->mode = SPI_MODE_0; /* TODO: Is this appropriate for right here? */ if (spi->max_speed_hz > MAX_SPI_SPEED_HZ) @@ -740,19 +752,6 @@ static int mrf24j40_probe(struct spi_device *spi) mutex_init(&devrec->buffer_mutex); init_completion(&devrec->tx_complete); - devrec->spi = spi; - spi_set_drvdata(spi, devrec); - - /* Register with the 802154 subsystem */ - - devrec->hw = ieee802154_alloc_hw(0, &mrf24j40_ops); - if (!devrec->hw) - goto err_ret; - - devrec->hw->priv = devrec; - devrec->hw->parent = &devrec->spi->dev; - devrec->hw->phy->supported.channels[0] = CHANNEL_MASK; - devrec->hw->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AFILT; dev_dbg(printdev(devrec), "registered mrf24j40\n"); ret = ieee802154_register_hw(devrec->hw); -- cgit v0.10.2 From a339e1849b9504b6e9631777e41ea2a240e58621 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 21 Sep 2015 11:24:24 +0200 Subject: mrf24j40: calling ieee802154_register_hw at last The function ieee802154_register_hw should always called at last. Currently we do hardware init and such things after register hardware into the subsystem. It could be that the subsystem already call driver operations while running hardware init. Reviewed-by: Stefan Schmidt Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index 1023cd2..de63cba 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -753,14 +753,9 @@ static int mrf24j40_probe(struct spi_device *spi) mutex_init(&devrec->buffer_mutex); init_completion(&devrec->tx_complete); - dev_dbg(printdev(devrec), "registered mrf24j40\n"); - ret = ieee802154_register_hw(devrec->hw); - if (ret) - goto err_register_device; - ret = mrf24j40_hw_init(devrec); if (ret) - goto err_hw_init; + goto err_register_device; ret = devm_request_threaded_irq(&spi->dev, spi->irq, @@ -772,14 +767,16 @@ static int mrf24j40_probe(struct spi_device *spi) if (ret) { dev_err(printdev(devrec), "Unable to get IRQ"); - goto err_irq; + goto err_register_device; } + dev_dbg(printdev(devrec), "registered mrf24j40\n"); + ret = ieee802154_register_hw(devrec->hw); + if (ret) + goto err_register_device; + return 0; -err_irq: -err_hw_init: - ieee802154_unregister_hw(devrec->hw); err_register_device: ieee802154_free_hw(devrec->hw); err_ret: -- cgit v0.10.2 From 78aedb6bfa43f43dacd8fc7b06452a87d33e3886 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 21 Sep 2015 11:24:25 +0200 Subject: mrf24j40: remove spi settings overwrite This patch removes spi settings while mrf24j40 probing. These settings cannot be overwrite while device probing where spi controller should be already configured. These settings need to be setup by device tree or platform data. Reviewed-by: Stefan Schmidt Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index de63cba..41d9f57 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -746,9 +746,11 @@ static int mrf24j40_probe(struct spi_device *spi) if (!devrec->buf) goto err_register_device; - spi->mode = SPI_MODE_0; /* TODO: Is this appropriate for right here? */ - if (spi->max_speed_hz > MAX_SPI_SPEED_HZ) - spi->max_speed_hz = MAX_SPI_SPEED_HZ; + if (spi->max_speed_hz > MAX_SPI_SPEED_HZ) { + dev_warn(&spi->dev, "spi clock above possible maximum: %d", + MAX_SPI_SPEED_HZ); + return -EINVAL; + } mutex_init(&devrec->buffer_mutex); init_completion(&devrec->tx_complete); -- cgit v0.10.2 From 2e6fd648b6b87e6a2289c875c6067acc3bd88b3e Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 21 Sep 2015 11:24:26 +0200 Subject: mrf24j40: add device-tree support This patch adds devicetree support to mrf24j40 with proper devicetree compatible strings. Reviewed-by: Stefan Schmidt Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/Documentation/devicetree/bindings/net/ieee802154/mrf24j40.txt b/Documentation/devicetree/bindings/net/ieee802154/mrf24j40.txt new file mode 100644 index 0000000..a4ed2ef --- /dev/null +++ b/Documentation/devicetree/bindings/net/ieee802154/mrf24j40.txt @@ -0,0 +1,20 @@ +* MRF24J40 IEEE 802.15.4 * + +Required properties: + - compatible: should be "microchip,mrf24j40", "microchip,mrf24j40ma", + or "microchip,mrf24j40mc" depends on your transceiver + board + - spi-max-frequency: maximal bus speed, should be set something under or equal + 10000000 + - reg: the chipselect index + - interrupts: the interrupt generated by the device. + +Example: + + mrf24j40ma@0 { + compatible = "microchip,mrf24j40ma"; + spi-max-frequency = <8500000>; + reg = <0>; + interrupts = <19 8>; + interrupt-parent = <&gpio3>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 310da42..6790ecc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6958,6 +6958,7 @@ M: Alan Ott L: linux-wpan@vger.kernel.org S: Maintained F: drivers/net/ieee802154/mrf24j40.c +F: Documentation/devicetree/bindings/net/ieee802154/mrf24j40.txt MSI LAPTOP SUPPORT M: "Lee, Chun-Yi" diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index 41d9f57..89150bd 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -799,6 +799,14 @@ static int mrf24j40_remove(struct spi_device *spi) return 0; } +static const struct of_device_id mrf24j40_of_match[] = { + { .compatible = "microchip,mrf24j40", .data = (void *)MRF24J40 }, + { .compatible = "microchip,mrf24j40ma", .data = (void *)MRF24J40MA }, + { .compatible = "microchip,mrf24j40mc", .data = (void *)MRF24J40MC }, + { }, +}; +MODULE_DEVICE_TABLE(of, mrf24j40_of_match); + static const struct spi_device_id mrf24j40_ids[] = { { "mrf24j40", MRF24J40 }, { "mrf24j40ma", MRF24J40MA }, @@ -809,6 +817,7 @@ MODULE_DEVICE_TABLE(spi, mrf24j40_ids); static struct spi_driver mrf24j40_driver = { .driver = { + .of_match_table = of_match_ptr(mrf24j40_of_match), .name = "mrf24j40", .owner = THIS_MODULE, }, -- cgit v0.10.2 From 766928fbf8c989f65184c3ed0d48fd3004213c46 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 21 Sep 2015 11:24:27 +0200 Subject: mrf24j40: add default channel setting Per default mrf24j40 has the channel 11 after reset. This patch adds the right phy default value for the channel setting. Reviewed-by: Stefan Schmidt Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index 89150bd..7c224cd 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -720,6 +720,11 @@ err_ret: return ret; } +static void mrf24j40_phy_setup(struct mrf24j40 *devrec) +{ + devrec->hw->phy->current_channel = 11; +} + static int mrf24j40_probe(struct spi_device *spi) { int ret = -ENOMEM; @@ -759,6 +764,8 @@ static int mrf24j40_probe(struct spi_device *spi) if (ret) goto err_register_device; + mrf24j40_phy_setup(devrec); + ret = devm_request_threaded_irq(&spi->dev, spi->irq, NULL, -- cgit v0.10.2 From d344c91280326469e5f9c5a3b1b92ef0d1c5d25f Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 21 Sep 2015 11:24:28 +0200 Subject: mrf24j40: add random extended addr generation The mrf24j40 has no source to get a permanent extended address. This patch will add a random generated permanent extended address source. Reviewed-by: Stefan Schmidt Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index 7c224cd..80b3c6e 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -722,6 +722,7 @@ err_ret: static void mrf24j40_phy_setup(struct mrf24j40 *devrec) { + ieee802154_random_extended_addr(&devrec->hw->phy->perm_extended_addr); devrec->hw->phy->current_channel = 11; } -- cgit v0.10.2 From 554b49493b89b068643b7360334b40d5f5ca8ff2 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 21 Sep 2015 11:24:29 +0200 Subject: mrf24j40: add more register defines For supporting regmap, this patch will add more register defines to prepare a full register dump by regmap debugfs. Reviewed-by: Stefan Schmidt Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index 80b3c6e..12f0606 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -29,21 +29,56 @@ #define REG_SADRL 0x03 /* Short address (low) */ #define REG_SADRH 0x04 /* Short address (high) */ #define REG_EADR0 0x05 /* Long address (low) (high is EADR7) */ +#define REG_EADR1 0x06 +#define REG_EADR2 0x07 +#define REG_EADR3 0x08 +#define REG_EADR4 0x09 +#define REG_EADR5 0x0A +#define REG_EADR6 0x0B +#define REG_EADR7 0x0C +#define REG_RXFLUSH 0x0D +#define REG_ORDER 0x10 #define REG_TXMCR 0x11 /* Transmit MAC control */ +#define REG_ACKTMOUT 0x12 +#define REG_ESLOTG1 0x13 +#define REG_SYMTICKL 0x14 +#define REG_SYMTICKH 0x15 #define REG_PACON0 0x16 /* Power Amplifier Control */ #define REG_PACON1 0x17 /* Power Amplifier Control */ #define REG_PACON2 0x18 /* Power Amplifier Control */ +#define REG_TXBCON0 0x1A #define REG_TXNCON 0x1B /* Transmit Normal FIFO Control */ +#define REG_TXG1CON 0x1C +#define REG_TXG2CON 0x1D +#define REG_ESLOTG23 0x1E +#define REG_ESLOTG45 0x1F +#define REG_ESLOTG67 0x20 +#define REG_TXPEND 0x21 +#define REG_WAKECON 0x22 +#define REG_FROMOFFSET 0x23 #define REG_TXSTAT 0x24 /* TX MAC Status Register */ +#define REG_TXBCON1 0x25 +#define REG_GATECLK 0x26 +#define REG_TXTIME 0x27 +#define REG_HSYMTMRL 0x28 +#define REG_HSYMTMRH 0x29 #define REG_SOFTRST 0x2A /* Soft Reset */ +#define REG_SECCON0 0x2C +#define REG_SECCON1 0x2D #define REG_TXSTBL 0x2E /* TX Stabilization */ +#define REG_RXSR 0x30 #define REG_INTSTAT 0x31 /* Interrupt Status */ #define REG_INTCON 0x32 /* Interrupt Control */ #define REG_GPIO 0x33 /* GPIO */ #define REG_TRISGPIO 0x34 /* GPIO direction */ +#define REG_SLPACK 0x35 #define REG_RFCTL 0x36 /* RF Control Mode Register */ +#define REG_SECCR2 0x37 +#define REG_BBREG0 0x38 #define REG_BBREG1 0x39 /* Baseband Registers */ #define REG_BBREG2 0x3A /* */ +#define REG_BBREG3 0x3B +#define REG_BBREG4 0x3C #define REG_BBREG6 0x3E /* */ #define REG_CCAEDTH 0x3F /* Energy Detection Threshold */ @@ -56,12 +91,45 @@ #define REG_RFCON6 0x206 #define REG_RFCON7 0x207 #define REG_RFCON8 0x208 +#define REG_SLPCAL0 0x209 +#define REG_SLPCAL1 0x20A +#define REG_SLPCAL2 0x20B +#define REG_RFSTATE 0x20F #define REG_RSSI 0x210 #define REG_SLPCON0 0x211 /* Sleep Clock Control Registers */ #define REG_SLPCON1 0x220 #define REG_WAKETIMEL 0x222 /* Wake-up Time Match Value Low */ #define REG_WAKETIMEH 0x223 /* Wake-up Time Match Value High */ +#define REG_REMCNTL 0x224 +#define REG_REMCNTH 0x225 +#define REG_MAINCNT0 0x226 +#define REG_MAINCNT1 0x227 +#define REG_MAINCNT2 0x228 +#define REG_MAINCNT3 0x229 #define REG_TESTMODE 0x22F /* Test mode */ +#define REG_ASSOEAR0 0x230 +#define REG_ASSOEAR1 0x231 +#define REG_ASSOEAR2 0x232 +#define REG_ASSOEAR3 0x233 +#define REG_ASSOEAR4 0x234 +#define REG_ASSOEAR5 0x235 +#define REG_ASSOEAR6 0x236 +#define REG_ASSOEAR7 0x237 +#define REG_ASSOSAR0 0x238 +#define REG_ASSOSAR1 0x239 +#define REG_UNONCE0 0x240 +#define REG_UNONCE1 0x241 +#define REG_UNONCE2 0x242 +#define REG_UNONCE3 0x243 +#define REG_UNONCE4 0x244 +#define REG_UNONCE5 0x245 +#define REG_UNONCE6 0x246 +#define REG_UNONCE7 0x247 +#define REG_UNONCE8 0x248 +#define REG_UNONCE9 0x249 +#define REG_UNONCE10 0x24A +#define REG_UNONCE11 0x24B +#define REG_UNONCE12 0x24C #define REG_RX_FIFO 0x300 /* Receive FIFO */ /* Device configuration: Only channels 11-26 on page 0 are supported. */ -- cgit v0.10.2 From b0156792001b7a19bc115dcc32aebb9b9a0b6a01 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 21 Sep 2015 11:24:30 +0200 Subject: mrf24j40: add regmap support This patch introduce regmap support for short and long address space of mrf24j40. It's only possible to use regmap_read/write/update_bits for long address range. This is because I added lowlevel bus operation because the write operation need to set the 12th bit to mark a register write, but regmap only supports to set bits for register write access in the first byte. We use other regmap register functions than read/write/update_bits, so this should be fine. Reviewed-by: Stefan Schmidt Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/Kconfig b/drivers/net/ieee802154/Kconfig index 5a614b2..ce5f1a2 100644 --- a/drivers/net/ieee802154/Kconfig +++ b/drivers/net/ieee802154/Kconfig @@ -43,6 +43,7 @@ config IEEE802154_MRF24J40 tristate "Microchip MRF24J40 transceiver driver" depends on IEEE802154_DRIVERS && MAC802154 depends on SPI + select REGMAP_SPI ---help--- Say Y here to enable the MRF24J20 SPI 802.15.4 wireless controller. diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index 12f0606..c8b7ce6 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -149,11 +150,22 @@ struct mrf24j40 { struct spi_device *spi; struct ieee802154_hw *hw; + struct regmap *regmap_short; + struct regmap *regmap_long; struct mutex buffer_mutex; /* only used to protect buf */ struct completion tx_complete; u8 *buf; /* 3 bytes. Used for SPI single-register transfers. */ }; +/* regmap information for short address register access */ +#define MRF24J40_SHORT_WRITE 0x01 +#define MRF24J40_SHORT_READ 0x00 +#define MRF24J40_SHORT_NUMREGS 0x3F + +/* regmap information for long address register access */ +#define MRF24J40_LONG_ACCESS 0x80 +#define MRF24J40_LONG_NUMREGS 0x38F + /* Read/Write SPI Commands for Short and Long Address registers. */ #define MRF24J40_READSHORT(reg) ((reg) << 1) #define MRF24J40_WRITESHORT(reg) ((reg) << 1 | 1) @@ -165,6 +177,287 @@ struct mrf24j40 { #define printdev(X) (&X->spi->dev) +static bool +mrf24j40_short_reg_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case REG_RXMCR: + case REG_PANIDL: + case REG_PANIDH: + case REG_SADRL: + case REG_SADRH: + case REG_EADR0: + case REG_EADR1: + case REG_EADR2: + case REG_EADR3: + case REG_EADR4: + case REG_EADR5: + case REG_EADR6: + case REG_EADR7: + case REG_RXFLUSH: + case REG_ORDER: + case REG_TXMCR: + case REG_ACKTMOUT: + case REG_ESLOTG1: + case REG_SYMTICKL: + case REG_SYMTICKH: + case REG_PACON0: + case REG_PACON1: + case REG_PACON2: + case REG_TXBCON0: + case REG_TXNCON: + case REG_TXG1CON: + case REG_TXG2CON: + case REG_ESLOTG23: + case REG_ESLOTG45: + case REG_ESLOTG67: + case REG_TXPEND: + case REG_WAKECON: + case REG_FROMOFFSET: + case REG_TXBCON1: + case REG_GATECLK: + case REG_TXTIME: + case REG_HSYMTMRL: + case REG_HSYMTMRH: + case REG_SOFTRST: + case REG_SECCON0: + case REG_SECCON1: + case REG_TXSTBL: + case REG_RXSR: + case REG_INTCON: + case REG_TRISGPIO: + case REG_GPIO: + case REG_RFCTL: + case REG_SLPACK: + case REG_BBREG0: + case REG_BBREG1: + case REG_BBREG2: + case REG_BBREG3: + case REG_BBREG4: + case REG_BBREG6: + case REG_CCAEDTH: + return true; + default: + return false; + } +} + +static bool +mrf24j40_short_reg_readable(struct device *dev, unsigned int reg) +{ + bool rc; + + /* all writeable are also readable */ + rc = mrf24j40_short_reg_writeable(dev, reg); + if (rc) + return rc; + + /* readonly regs */ + switch (reg) { + case REG_TXSTAT: + case REG_INTSTAT: + return true; + default: + return false; + } +} + +static bool +mrf24j40_short_reg_volatile(struct device *dev, unsigned int reg) +{ + /* can be changed during runtime */ + switch (reg) { + case REG_TXSTAT: + case REG_INTSTAT: + case REG_RXFLUSH: + case REG_TXNCON: + case REG_SOFTRST: + case REG_RFCTL: + case REG_TXBCON0: + case REG_TXG1CON: + case REG_TXG2CON: + case REG_TXBCON1: + case REG_SECCON0: + case REG_RXSR: + case REG_SLPACK: + case REG_SECCR2: + case REG_BBREG6: + /* use them in spi_async and regmap so it's volatile */ + case REG_BBREG1: + return true; + default: + return false; + } +} + +static bool +mrf24j40_short_reg_precious(struct device *dev, unsigned int reg) +{ + /* don't clear irq line on read */ + switch (reg) { + case REG_INTSTAT: + return true; + default: + return false; + } +} + +static const struct regmap_config mrf24j40_short_regmap = { + .name = "mrf24j40_short", + .reg_bits = 7, + .val_bits = 8, + .pad_bits = 1, + .write_flag_mask = MRF24J40_SHORT_WRITE, + .read_flag_mask = MRF24J40_SHORT_READ, + .cache_type = REGCACHE_RBTREE, + .max_register = MRF24J40_SHORT_NUMREGS, + .writeable_reg = mrf24j40_short_reg_writeable, + .readable_reg = mrf24j40_short_reg_readable, + .volatile_reg = mrf24j40_short_reg_volatile, + .precious_reg = mrf24j40_short_reg_precious, +}; + +static bool +mrf24j40_long_reg_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case REG_RFCON0: + case REG_RFCON1: + case REG_RFCON2: + case REG_RFCON3: + case REG_RFCON5: + case REG_RFCON6: + case REG_RFCON7: + case REG_RFCON8: + case REG_SLPCAL2: + case REG_SLPCON0: + case REG_SLPCON1: + case REG_WAKETIMEL: + case REG_WAKETIMEH: + case REG_REMCNTL: + case REG_REMCNTH: + case REG_MAINCNT0: + case REG_MAINCNT1: + case REG_MAINCNT2: + case REG_MAINCNT3: + case REG_TESTMODE: + case REG_ASSOEAR0: + case REG_ASSOEAR1: + case REG_ASSOEAR2: + case REG_ASSOEAR3: + case REG_ASSOEAR4: + case REG_ASSOEAR5: + case REG_ASSOEAR6: + case REG_ASSOEAR7: + case REG_ASSOSAR0: + case REG_ASSOSAR1: + case REG_UNONCE0: + case REG_UNONCE1: + case REG_UNONCE2: + case REG_UNONCE3: + case REG_UNONCE4: + case REG_UNONCE5: + case REG_UNONCE6: + case REG_UNONCE7: + case REG_UNONCE8: + case REG_UNONCE9: + case REG_UNONCE10: + case REG_UNONCE11: + case REG_UNONCE12: + return true; + default: + return false; + } +} + +static bool +mrf24j40_long_reg_readable(struct device *dev, unsigned int reg) +{ + bool rc; + + /* all writeable are also readable */ + rc = mrf24j40_long_reg_writeable(dev, reg); + if (rc) + return rc; + + /* readonly regs */ + switch (reg) { + case REG_SLPCAL0: + case REG_SLPCAL1: + case REG_RFSTATE: + case REG_RSSI: + return true; + default: + return false; + } +} + +static bool +mrf24j40_long_reg_volatile(struct device *dev, unsigned int reg) +{ + /* can be changed during runtime */ + switch (reg) { + case REG_SLPCAL0: + case REG_SLPCAL1: + case REG_SLPCAL2: + case REG_RFSTATE: + case REG_RSSI: + case REG_MAINCNT3: + return true; + default: + return false; + } +} + +static const struct regmap_config mrf24j40_long_regmap = { + .name = "mrf24j40_long", + .reg_bits = 11, + .val_bits = 8, + .pad_bits = 5, + .write_flag_mask = MRF24J40_LONG_ACCESS, + .read_flag_mask = MRF24J40_LONG_ACCESS, + .cache_type = REGCACHE_RBTREE, + .max_register = MRF24J40_LONG_NUMREGS, + .writeable_reg = mrf24j40_long_reg_writeable, + .readable_reg = mrf24j40_long_reg_readable, + .volatile_reg = mrf24j40_long_reg_volatile, +}; + +static int mrf24j40_long_regmap_write(void *context, const void *data, + size_t count) +{ + struct spi_device *spi = context; + u8 buf[3]; + + if (count > 3) + return -EINVAL; + + /* regmap supports read/write mask only in frist byte + * long write access need to set the 12th bit, so we + * make special handling for write. + */ + memcpy(buf, data, count); + buf[1] |= (1 << 4); + + return spi_write(spi, buf, count); +} + +static int +mrf24j40_long_regmap_read(void *context, const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct spi_device *spi = context; + + return spi_write_then_read(spi, reg, reg_size, val, val_size); +} + +static const struct regmap_bus mrf24j40_long_regmap_bus = { + .write = mrf24j40_long_regmap_write, + .read = mrf24j40_long_regmap_read, + .reg_format_endian_default = REGMAP_ENDIAN_BIG, + .val_format_endian_default = REGMAP_ENDIAN_BIG, +}; + static int write_short_reg(struct mrf24j40 *devrec, u8 reg, u8 value) { int ret; @@ -816,6 +1109,25 @@ static int mrf24j40_probe(struct spi_device *spi) devrec->hw->phy->supported.channels[0] = CHANNEL_MASK; devrec->hw->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AFILT; + devrec->regmap_short = devm_regmap_init_spi(spi, + &mrf24j40_short_regmap); + if (IS_ERR(devrec->regmap_short)) { + ret = PTR_ERR(devrec->regmap_short); + dev_err(&spi->dev, "Failed to allocate short register map: %d\n", + ret); + goto err_register_device; + } + + devrec->regmap_long = devm_regmap_init(&spi->dev, + &mrf24j40_long_regmap_bus, + spi, &mrf24j40_long_regmap); + if (IS_ERR(devrec->regmap_long)) { + ret = PTR_ERR(devrec->regmap_long); + dev_err(&spi->dev, "Failed to allocate long register map: %d\n", + ret); + goto err_register_device; + } + devrec->buf = devm_kzalloc(&spi->dev, 3, GFP_KERNEL); if (!devrec->buf) goto err_register_device; -- cgit v0.10.2 From 42c7148e06661ae0ca1bb511e1b98878b5f522f8 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 21 Sep 2015 11:24:31 +0200 Subject: mrf24j40: use regmap for register access This patch uses the regmap functions for transceiver register settings where it's possible. This means everything except the hotpaths like receive/transmit handling. Reviewed-by: Stefan Schmidt Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index c8b7ce6..ca98b5c 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -543,35 +543,6 @@ static int read_long_reg(struct mrf24j40 *devrec, u16 reg, u8 *value) return ret; } -static int write_long_reg(struct mrf24j40 *devrec, u16 reg, u8 val) -{ - int ret; - u16 cmd; - struct spi_message msg; - struct spi_transfer xfer = { - .len = 3, - .tx_buf = devrec->buf, - .rx_buf = devrec->buf, - }; - - spi_message_init(&msg); - spi_message_add_tail(&xfer, &msg); - - cmd = MRF24J40_WRITELONG(reg); - mutex_lock(&devrec->buffer_mutex); - devrec->buf[0] = cmd >> 8 & 0xff; - devrec->buf[1] = cmd & 0xff; - devrec->buf[2] = val; - - ret = spi_sync(devrec->spi, &msg); - if (ret) - dev_err(printdev(devrec), - "SPI write Failed for long register 0x%hx\n", reg); - - mutex_unlock(&devrec->buffer_mutex); - return ret; -} - /* This function relies on an undocumented write method. Once a write command and address is set, as many bytes of data as desired can be clocked into the device. The datasheet only shows setting one byte at a time. */ @@ -755,33 +726,23 @@ static int mrf24j40_ed(struct ieee802154_hw *hw, u8 *level) static int mrf24j40_start(struct ieee802154_hw *hw) { struct mrf24j40 *devrec = hw->priv; - u8 val; - int ret; dev_dbg(printdev(devrec), "start\n"); - ret = read_short_reg(devrec, REG_INTCON, &val); - if (ret) - return ret; - val &= ~(0x1|0x8); /* Clear TXNIE and RXIE. Enable interrupts */ - write_short_reg(devrec, REG_INTCON, val); - - return 0; + /* Clear TXNIE and RXIE. Enable interrupts */ + return regmap_update_bits(devrec->regmap_short, REG_INTCON, + 0x01 | 0x08, 0x00); } static void mrf24j40_stop(struct ieee802154_hw *hw) { struct mrf24j40 *devrec = hw->priv; - u8 val; - int ret; dev_dbg(printdev(devrec), "stop\n"); - ret = read_short_reg(devrec, REG_INTCON, &val); - if (ret) - return; - val |= 0x1|0x8; /* Set TXNIE and RXIE. Disable Interrupts */ - write_short_reg(devrec, REG_INTCON, val); + /* Set TXNIE and RXIE. Disable Interrupts */ + regmap_update_bits(devrec->regmap_short, REG_INTCON, 0x01 | 0x08, + 0x01 | 0x08); } static int mrf24j40_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel) @@ -798,20 +759,20 @@ static int mrf24j40_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel) /* Set Channel TODO */ val = (channel-11) << 4 | 0x03; - write_long_reg(devrec, REG_RFCON0, val); + ret = regmap_update_bits(devrec->regmap_long, REG_RFCON0, 0xf0, val); + if (ret) + return ret; /* RF Reset */ - ret = read_short_reg(devrec, REG_RFCTL, &val); + ret = regmap_update_bits(devrec->regmap_short, REG_RFCTL, 0x04, 0x04); if (ret) return ret; - val |= 0x04; - write_short_reg(devrec, REG_RFCTL, val); - val &= ~0x04; - write_short_reg(devrec, REG_RFCTL, val); - udelay(SET_CHANNEL_DELAY_US); /* per datasheet */ + ret = regmap_update_bits(devrec->regmap_short, REG_RFCTL, 0x04, 0x00); + if (!ret) + udelay(SET_CHANNEL_DELAY_US); /* per datasheet */ - return 0; + return ret; } static int mrf24j40_filter(struct ieee802154_hw *hw, @@ -829,8 +790,8 @@ static int mrf24j40_filter(struct ieee802154_hw *hw, addrh = le16_to_cpu(filt->short_addr) >> 8 & 0xff; addrl = le16_to_cpu(filt->short_addr) & 0xff; - write_short_reg(devrec, REG_SADRH, addrh); - write_short_reg(devrec, REG_SADRL, addrl); + regmap_write(devrec->regmap_short, REG_SADRH, addrh); + regmap_write(devrec->regmap_short, REG_SADRL, addrl); dev_dbg(printdev(devrec), "Set short addr to %04hx\n", filt->short_addr); } @@ -841,7 +802,8 @@ static int mrf24j40_filter(struct ieee802154_hw *hw, memcpy(addr, &filt->ieee_addr, 8); for (i = 0; i < 8; i++) - write_short_reg(devrec, REG_EADR0 + i, addr[i]); + regmap_write(devrec->regmap_short, REG_EADR0 + i, + addr[i]); #ifdef DEBUG pr_debug("Set long addr to: "); @@ -857,8 +819,8 @@ static int mrf24j40_filter(struct ieee802154_hw *hw, panidh = le16_to_cpu(filt->pan_id) >> 8 & 0xff; panidl = le16_to_cpu(filt->pan_id) & 0xff; - write_short_reg(devrec, REG_PANIDH, panidh); - write_short_reg(devrec, REG_PANIDL, panidl); + regmap_write(devrec->regmap_short, REG_PANIDH, panidh); + regmap_write(devrec->regmap_short, REG_PANIDL, panidl); dev_dbg(printdev(devrec), "Set PANID to %04hx\n", filt->pan_id); } @@ -868,14 +830,14 @@ static int mrf24j40_filter(struct ieee802154_hw *hw, u8 val; int ret; - ret = read_short_reg(devrec, REG_RXMCR, &val); - if (ret) - return ret; if (filt->pan_coord) - val |= 0x8; + val = 0x8; else - val &= ~0x8; - write_short_reg(devrec, REG_RXMCR, val); + val = 0x0; + ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR, 0x8, + val); + if (ret) + return ret; /* REG_SLOTTED is maintained as default (unslotted/CSMA-CA). * REG_ORDER is maintained as default (no beacon/superframe). @@ -976,80 +938,73 @@ out: static int mrf24j40_hw_init(struct mrf24j40 *devrec) { int ret; - u8 val; /* Initialize the device. From datasheet section 3.2: Initialization. */ - ret = write_short_reg(devrec, REG_SOFTRST, 0x07); + ret = regmap_write(devrec->regmap_short, REG_SOFTRST, 0x07); if (ret) goto err_ret; - ret = write_short_reg(devrec, REG_PACON2, 0x98); + ret = regmap_write(devrec->regmap_short, REG_PACON2, 0x98); if (ret) goto err_ret; - ret = write_short_reg(devrec, REG_TXSTBL, 0x95); + ret = regmap_write(devrec->regmap_short, REG_TXSTBL, 0x95); if (ret) goto err_ret; - ret = write_long_reg(devrec, REG_RFCON0, 0x03); + ret = regmap_write(devrec->regmap_long, REG_RFCON0, 0x03); if (ret) goto err_ret; - ret = write_long_reg(devrec, REG_RFCON1, 0x01); + ret = regmap_write(devrec->regmap_long, REG_RFCON1, 0x01); if (ret) goto err_ret; - ret = write_long_reg(devrec, REG_RFCON2, 0x80); + ret = regmap_write(devrec->regmap_long, REG_RFCON2, 0x80); if (ret) goto err_ret; - ret = write_long_reg(devrec, REG_RFCON6, 0x90); + ret = regmap_write(devrec->regmap_long, REG_RFCON6, 0x90); if (ret) goto err_ret; - ret = write_long_reg(devrec, REG_RFCON7, 0x80); + ret = regmap_write(devrec->regmap_long, REG_RFCON7, 0x80); if (ret) goto err_ret; - ret = write_long_reg(devrec, REG_RFCON8, 0x10); + ret = regmap_write(devrec->regmap_long, REG_RFCON8, 0x10); if (ret) goto err_ret; - ret = write_long_reg(devrec, REG_SLPCON1, 0x21); + ret = regmap_write(devrec->regmap_long, REG_SLPCON1, 0x21); if (ret) goto err_ret; - ret = write_short_reg(devrec, REG_BBREG2, 0x80); + ret = regmap_write(devrec->regmap_short, REG_BBREG2, 0x80); if (ret) goto err_ret; - ret = write_short_reg(devrec, REG_CCAEDTH, 0x60); + ret = regmap_write(devrec->regmap_short, REG_CCAEDTH, 0x60); if (ret) goto err_ret; - ret = write_short_reg(devrec, REG_BBREG6, 0x40); + ret = regmap_write(devrec->regmap_short, REG_BBREG6, 0x40); if (ret) goto err_ret; - ret = write_short_reg(devrec, REG_RFCTL, 0x04); + ret = regmap_write(devrec->regmap_short, REG_RFCTL, 0x04); if (ret) goto err_ret; - ret = write_short_reg(devrec, REG_RFCTL, 0x0); + ret = regmap_write(devrec->regmap_short, REG_RFCTL, 0x0); if (ret) goto err_ret; udelay(192); /* Set RX Mode. RXMCR<1:0>: 0x0 normal, 0x1 promisc, 0x2 error */ - ret = read_short_reg(devrec, REG_RXMCR, &val); - if (ret) - goto err_ret; - - val &= ~0x3; /* Clear RX mode (normal) */ - - ret = write_short_reg(devrec, REG_RXMCR, val); + ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR, 0x03, 0x00); if (ret) goto err_ret; @@ -1057,22 +1012,20 @@ static int mrf24j40_hw_init(struct mrf24j40 *devrec) /* Enable external amplifier. * From MRF24J40MC datasheet section 1.3: Operation. */ - read_long_reg(devrec, REG_TESTMODE, &val); - val |= 0x7; /* Configure GPIO 0-2 to control amplifier */ - write_long_reg(devrec, REG_TESTMODE, val); + regmap_update_bits(devrec->regmap_long, REG_TESTMODE, 0x07, + 0x07); - read_short_reg(devrec, REG_TRISGPIO, &val); - val |= 0x8; /* Set GPIO3 as output. */ - write_short_reg(devrec, REG_TRISGPIO, val); + /* Set GPIO3 as output. */ + regmap_update_bits(devrec->regmap_short, REG_TRISGPIO, 0x08, + 0x08); - read_short_reg(devrec, REG_GPIO, &val); - val |= 0x8; /* Set GPIO3 HIGH to enable U5 voltage regulator */ - write_short_reg(devrec, REG_GPIO, val); + /* Set GPIO3 HIGH to enable U5 voltage regulator */ + regmap_update_bits(devrec->regmap_short, REG_GPIO, 0x08, 0x08); /* Reduce TX pwr to meet FCC requirements. * From MRF24J40MC datasheet section 3.1.1 */ - write_long_reg(devrec, REG_RFCON3, 0x28); + regmap_write(devrec->regmap_long, REG_RFCON3, 0x28); } return 0; -- cgit v0.10.2 From ab40ff7508d2f54ed03b99523493c46250649c52 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 21 Sep 2015 11:24:32 +0200 Subject: mrf24j40: change to frame delivery with crc This patch changes the frame delivery to mac802154 with crc. This is useful for monitor interface types which deliver the crc to userspace. Reviewed-by: Stefan Schmidt Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index ca98b5c..788adb6 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -881,9 +881,6 @@ static int mrf24j40_handle_rx(struct mrf24j40 *devrec) goto out; } - /* Cut off the checksum */ - skb_trim(skb, len-2); - /* TODO: Other drivers call ieee20154_rx_irqsafe() here (eg: cc2040, * also from a workqueue). I think irqsafe is not necessary here. * Can someone confirm? */ @@ -1060,7 +1057,7 @@ static int mrf24j40_probe(struct spi_device *spi) devrec->hw = hw; devrec->hw->parent = &spi->dev; devrec->hw->phy->supported.channels[0] = CHANNEL_MASK; - devrec->hw->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AFILT; + devrec->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT; devrec->regmap_short = devm_regmap_init_spi(spi, &mrf24j40_short_regmap); -- cgit v0.10.2 From 79750ac4257763ff595a8b2cdc7ba580f0b0c8e0 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 21 Sep 2015 11:24:33 +0200 Subject: ieee802154: add helpers for frame control checks This patch introduce two static inline functions. The first to get the frame control field from an sk_buff. The second is for checking on the acknowledgment request bit on the frame control field. Later we can introduce more functions to check on the frame control fields. These will deprecate the current behaviour which requires a host-byteorder conversion and manually bit handling. Reviewed-by: Stefan Schmidt Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/include/linux/ieee802154.h b/include/linux/ieee802154.h index 205ce4e..aca228b 100644 --- a/include/linux/ieee802154.h +++ b/include/linux/ieee802154.h @@ -25,6 +25,8 @@ #include #include +#include +#include #include #define IEEE802154_MTU 127 @@ -218,6 +220,7 @@ enum { /* frame control handling */ #define IEEE802154_FCTL_FTYPE 0x0003 +#define IEEE802154_FCTL_ACKREQ 0x0020 #define IEEE802154_FCTL_INTRA_PAN 0x0040 #define IEEE802154_FTYPE_DATA 0x0001 @@ -233,6 +236,15 @@ static inline int ieee802154_is_data(__le16 fc) } /** + * ieee802154_is_ackreq - check if acknowledgment request bit is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline bool ieee802154_is_ackreq(__le16 fc) +{ + return fc & cpu_to_le16(IEEE802154_FCTL_ACKREQ); +} + +/** * ieee802154_is_intra_pan - check if intra pan id communication * @fc: frame control bytes in little-endian byteorder */ -- cgit v0.10.2 From 6844a0e4debd67c9bcf6a8fed3c7f9c24413c349 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 21 Sep 2015 11:24:34 +0200 Subject: mrf24j40: rework tx handling to async tx handling This patch reworks the current transmit API to spi_async handling. We removed the error handling check, because mac802154 has no chance to report it. Also the transmit timeout handling can't be handled by xmit async handling, for this usecase we need to implement the netdev watchdog. These are all unlikely cases which we drop now and should be provided by netdev watchdog. We also drop the bit setting for TXNACKREQ at register TXNCON, this is not necessary. The TXNCON register should set only once for each frame, previous settings doesn't matter. Reviewed-by: Stefan Schmidt Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index 788adb6..3cf0625 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -152,8 +152,22 @@ struct mrf24j40 { struct regmap *regmap_short; struct regmap *regmap_long; + + /* for writing txfifo */ + struct spi_message tx_msg; + u8 tx_hdr_buf[2]; + struct spi_transfer tx_hdr_trx; + u8 tx_len_buf[2]; + struct spi_transfer tx_len_trx; + struct spi_transfer tx_buf_trx; + struct sk_buff *tx_skb; + + /* post transmit message to send frame out */ + struct spi_message tx_post_msg; + u8 tx_post_buf[2]; + struct spi_transfer tx_post_trx; + struct mutex buffer_mutex; /* only used to protect buf */ - struct completion tx_complete; u8 *buf; /* 3 bytes. Used for SPI single-register transfers. */ }; @@ -543,28 +557,33 @@ static int read_long_reg(struct mrf24j40 *devrec, u16 reg, u8 *value) return ret; } +static void write_tx_buf_complete(void *context) +{ + struct mrf24j40 *devrec = context; + __le16 fc = ieee802154_get_fc_from_skb(devrec->tx_skb); + u8 val = 0x01; + int ret; + + if (ieee802154_is_ackreq(fc)) + val |= 0x04; + + devrec->tx_post_msg.complete = NULL; + devrec->tx_post_buf[0] = MRF24J40_WRITESHORT(REG_TXNCON); + devrec->tx_post_buf[1] = val; + + ret = spi_async(devrec->spi, &devrec->tx_post_msg); + if (ret) + dev_err(printdev(devrec), "SPI write Failed for transmit buf\n"); +} + /* This function relies on an undocumented write method. Once a write command and address is set, as many bytes of data as desired can be clocked into the device. The datasheet only shows setting one byte at a time. */ static int write_tx_buf(struct mrf24j40 *devrec, u16 reg, const u8 *data, size_t length) { - int ret; u16 cmd; - u8 lengths[2]; - struct spi_message msg; - struct spi_transfer addr_xfer = { - .len = 2, - .tx_buf = devrec->buf, - }; - struct spi_transfer lengths_xfer = { - .len = 2, - .tx_buf = &lengths, /* TODO: Is DMA really required for SPI? */ - }; - struct spi_transfer data_xfer = { - .len = length, - .tx_buf = data, - }; + int ret; /* Range check the length. 2 bytes are used for the length fields.*/ if (length > TX_FIFO_SIZE-2) { @@ -572,26 +591,31 @@ static int write_tx_buf(struct mrf24j40 *devrec, u16 reg, length = TX_FIFO_SIZE-2; } - spi_message_init(&msg); - spi_message_add_tail(&addr_xfer, &msg); - spi_message_add_tail(&lengths_xfer, &msg); - spi_message_add_tail(&data_xfer, &msg); - cmd = MRF24J40_WRITELONG(reg); - mutex_lock(&devrec->buffer_mutex); - devrec->buf[0] = cmd >> 8 & 0xff; - devrec->buf[1] = cmd & 0xff; - lengths[0] = 0x0; /* Header Length. Set to 0 for now. TODO */ - lengths[1] = length; /* Total length */ - - ret = spi_sync(devrec->spi, &msg); + devrec->tx_hdr_buf[0] = cmd >> 8 & 0xff; + devrec->tx_hdr_buf[1] = cmd & 0xff; + devrec->tx_len_buf[0] = 0x0; /* Header Length. Set to 0 for now. TODO */ + devrec->tx_len_buf[1] = length; /* Total length */ + devrec->tx_buf_trx.tx_buf = data; + devrec->tx_buf_trx.len = length; + + ret = spi_async(devrec->spi, &devrec->tx_msg); if (ret) dev_err(printdev(devrec), "SPI write Failed for TX buf\n"); - mutex_unlock(&devrec->buffer_mutex); return ret; } +static int mrf24j40_tx(struct ieee802154_hw *hw, struct sk_buff *skb) +{ + struct mrf24j40 *devrec = hw->priv; + + dev_dbg(printdev(devrec), "tx packet of %d bytes\n", skb->len); + devrec->tx_skb = skb; + + return write_tx_buf(devrec, 0x000, skb->data, skb->len); +} + static int mrf24j40_read_rx_buf(struct mrf24j40 *devrec, u8 *data, u8 *len, u8 *lqi) { @@ -664,57 +688,6 @@ out: return ret; } -static int mrf24j40_tx(struct ieee802154_hw *hw, struct sk_buff *skb) -{ - struct mrf24j40 *devrec = hw->priv; - u8 val; - int ret = 0; - - dev_dbg(printdev(devrec), "tx packet of %d bytes\n", skb->len); - - ret = write_tx_buf(devrec, 0x000, skb->data, skb->len); - if (ret) - goto err; - - reinit_completion(&devrec->tx_complete); - - /* Set TXNTRIG bit of TXNCON to send packet */ - ret = read_short_reg(devrec, REG_TXNCON, &val); - if (ret) - goto err; - val |= 0x1; - /* Set TXNACKREQ if the ACK bit is set in the packet. */ - if (skb->data[0] & IEEE802154_FC_ACK_REQ) - val |= 0x4; - write_short_reg(devrec, REG_TXNCON, val); - - /* Wait for the device to send the TX complete interrupt. */ - ret = wait_for_completion_interruptible_timeout( - &devrec->tx_complete, - 5 * HZ); - if (ret == -ERESTARTSYS) - goto err; - if (ret == 0) { - dev_warn(printdev(devrec), "Timeout waiting for TX interrupt\n"); - ret = -ETIMEDOUT; - goto err; - } - - /* Check for send error from the device. */ - ret = read_short_reg(devrec, REG_TXSTAT, &val); - if (ret) - goto err; - if (val & 0x1) { - dev_dbg(printdev(devrec), "Error Sending. Retry count exceeded\n"); - ret = -ECOMM; /* TODO: Better error code ? */ - } else - dev_dbg(printdev(devrec), "Packet Sent\n"); - -err: - - return ret; -} - static int mrf24j40_ed(struct ieee802154_hw *hw, u8 *level) { /* TODO: */ @@ -901,7 +874,7 @@ out: static const struct ieee802154_ops mrf24j40_ops = { .owner = THIS_MODULE, - .xmit_sync = mrf24j40_tx, + .xmit_async = mrf24j40_tx, .ed = mrf24j40_ed, .start = mrf24j40_start, .stop = mrf24j40_stop, @@ -922,7 +895,7 @@ static irqreturn_t mrf24j40_isr(int irq, void *data) /* Check for TX complete */ if (intstat & 0x1) - complete(&devrec->tx_complete); + ieee802154_xmit_complete(devrec->hw, devrec->tx_skb, false); /* Check for Rx */ if (intstat & 0x8) @@ -1031,6 +1004,27 @@ err_ret: return ret; } +static void +mrf24j40_setup_tx_spi_messages(struct mrf24j40 *devrec) +{ + spi_message_init(&devrec->tx_msg); + devrec->tx_msg.context = devrec; + devrec->tx_msg.complete = write_tx_buf_complete; + devrec->tx_hdr_trx.len = 2; + devrec->tx_hdr_trx.tx_buf = devrec->tx_hdr_buf; + spi_message_add_tail(&devrec->tx_hdr_trx, &devrec->tx_msg); + devrec->tx_len_trx.len = 2; + devrec->tx_len_trx.tx_buf = devrec->tx_len_buf; + spi_message_add_tail(&devrec->tx_len_trx, &devrec->tx_msg); + spi_message_add_tail(&devrec->tx_buf_trx, &devrec->tx_msg); + + spi_message_init(&devrec->tx_post_msg); + devrec->tx_post_msg.context = devrec; + devrec->tx_post_trx.len = 2; + devrec->tx_post_trx.tx_buf = devrec->tx_post_buf; + spi_message_add_tail(&devrec->tx_post_trx, &devrec->tx_post_msg); +} + static void mrf24j40_phy_setup(struct mrf24j40 *devrec) { ieee802154_random_extended_addr(&devrec->hw->phy->perm_extended_addr); @@ -1059,6 +1053,8 @@ static int mrf24j40_probe(struct spi_device *spi) devrec->hw->phy->supported.channels[0] = CHANNEL_MASK; devrec->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT; + mrf24j40_setup_tx_spi_messages(devrec); + devrec->regmap_short = devm_regmap_init_spi(spi, &mrf24j40_short_regmap); if (IS_ERR(devrec->regmap_short)) { @@ -1089,7 +1085,6 @@ static int mrf24j40_probe(struct spi_device *spi) } mutex_init(&devrec->buffer_mutex); - init_completion(&devrec->tx_complete); ret = mrf24j40_hw_init(devrec); if (ret) -- cgit v0.10.2 From c91a301162899ee100c1eab397958cb9aef50d0c Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 21 Sep 2015 11:24:35 +0200 Subject: mrf24j40: rework rx handling to async rx handling This patch prepares that we can do the receive handling inside interrupt context by using spi_async. This is necessary for introduce a non-threaded irq handling. Also we drop the bit setting for "RXDECINV" at register "BBREG1", we do a driectly full write of register "BBREG1", because it contains the bit RXDECINV only. Reviewed-by: Stefan Schmidt Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index 3cf0625..0cb251e 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -167,6 +167,20 @@ struct mrf24j40 { u8 tx_post_buf[2]; struct spi_transfer tx_post_trx; + /* for protect/unprotect/read length rxfifo */ + struct spi_message rx_msg; + u8 rx_buf[3]; + struct spi_transfer rx_trx; + + /* receive handling */ + struct spi_message rx_buf_msg; + u8 rx_addr_buf[2]; + struct spi_transfer rx_addr_trx; + u8 rx_lqi_buf[2]; + struct spi_transfer rx_lqi_trx; + u8 rx_fifo_buf[RX_FIFO_SIZE]; + struct spi_transfer rx_fifo_buf_trx; + struct mutex buffer_mutex; /* only used to protect buf */ u8 *buf; /* 3 bytes. Used for SPI single-register transfers. */ }; @@ -472,32 +486,6 @@ static const struct regmap_bus mrf24j40_long_regmap_bus = { .val_format_endian_default = REGMAP_ENDIAN_BIG, }; -static int write_short_reg(struct mrf24j40 *devrec, u8 reg, u8 value) -{ - int ret; - struct spi_message msg; - struct spi_transfer xfer = { - .len = 2, - .tx_buf = devrec->buf, - .rx_buf = devrec->buf, - }; - - spi_message_init(&msg); - spi_message_add_tail(&xfer, &msg); - - mutex_lock(&devrec->buffer_mutex); - devrec->buf[0] = MRF24J40_WRITESHORT(reg); - devrec->buf[1] = value; - - ret = spi_sync(devrec->spi, &msg); - if (ret) - dev_err(printdev(devrec), - "SPI write Failed for short register 0x%hhx\n", reg); - - mutex_unlock(&devrec->buffer_mutex); - return ret; -} - static int read_short_reg(struct mrf24j40 *devrec, u8 reg, u8 *val) { int ret = -1; @@ -526,37 +514,6 @@ static int read_short_reg(struct mrf24j40 *devrec, u8 reg, u8 *val) return ret; } -static int read_long_reg(struct mrf24j40 *devrec, u16 reg, u8 *value) -{ - int ret; - u16 cmd; - struct spi_message msg; - struct spi_transfer xfer = { - .len = 3, - .tx_buf = devrec->buf, - .rx_buf = devrec->buf, - }; - - spi_message_init(&msg); - spi_message_add_tail(&xfer, &msg); - - cmd = MRF24J40_READLONG(reg); - mutex_lock(&devrec->buffer_mutex); - devrec->buf[0] = cmd >> 8 & 0xff; - devrec->buf[1] = cmd & 0xff; - devrec->buf[2] = 0; - - ret = spi_sync(devrec->spi, &msg); - if (ret) - dev_err(printdev(devrec), - "SPI read Failed for long register 0x%hx\n", reg); - else - *value = devrec->buf[2]; - - mutex_unlock(&devrec->buffer_mutex); - return ret; -} - static void write_tx_buf_complete(void *context) { struct mrf24j40 *devrec = context; @@ -616,78 +573,6 @@ static int mrf24j40_tx(struct ieee802154_hw *hw, struct sk_buff *skb) return write_tx_buf(devrec, 0x000, skb->data, skb->len); } -static int mrf24j40_read_rx_buf(struct mrf24j40 *devrec, - u8 *data, u8 *len, u8 *lqi) -{ - u8 rx_len; - u8 addr[2]; - u8 lqi_rssi[2]; - u16 cmd; - int ret; - struct spi_message msg; - struct spi_transfer addr_xfer = { - .len = 2, - .tx_buf = &addr, - }; - struct spi_transfer data_xfer = { - .len = 0x0, /* set below */ - .rx_buf = data, - }; - struct spi_transfer status_xfer = { - .len = 2, - .rx_buf = &lqi_rssi, - }; - - /* Get the length of the data in the RX FIFO. The length in this - * register exclues the 1-byte length field at the beginning. */ - ret = read_long_reg(devrec, REG_RX_FIFO, &rx_len); - if (ret) - goto out; - - /* Range check the RX FIFO length, accounting for the one-byte - * length field at the beginning. */ - if (rx_len > RX_FIFO_SIZE-1) { - dev_err(printdev(devrec), "Invalid length read from device. Performing short read.\n"); - rx_len = RX_FIFO_SIZE-1; - } - - if (rx_len > *len) { - /* Passed in buffer wasn't big enough. Should never happen. */ - dev_err(printdev(devrec), "Buffer not big enough. Performing short read\n"); - rx_len = *len; - } - - /* Set up the commands to read the data. */ - cmd = MRF24J40_READLONG(REG_RX_FIFO+1); - addr[0] = cmd >> 8 & 0xff; - addr[1] = cmd & 0xff; - data_xfer.len = rx_len; - - spi_message_init(&msg); - spi_message_add_tail(&addr_xfer, &msg); - spi_message_add_tail(&data_xfer, &msg); - spi_message_add_tail(&status_xfer, &msg); - - ret = spi_sync(devrec->spi, &msg); - if (ret) { - dev_err(printdev(devrec), "SPI RX Buffer Read Failed.\n"); - goto out; - } - - *lqi = lqi_rssi[0]; - *len = rx_len; - -#ifdef DEBUG - print_hex_dump(KERN_DEBUG, "mrf24j40 rx: ", - DUMP_PREFIX_OFFSET, 16, 1, data, *len, 0); - pr_debug("mrf24j40 rx: lqi: %02hhx rssi: %02hhx\n", - lqi_rssi[0], lqi_rssi[1]); -#endif - -out: - return ret; -} - static int mrf24j40_ed(struct ieee802154_hw *hw, u8 *level) { /* TODO: */ @@ -823,53 +708,98 @@ static int mrf24j40_filter(struct ieee802154_hw *hw, return 0; } -static int mrf24j40_handle_rx(struct mrf24j40 *devrec) +static void mrf24j40_handle_rx_read_buf_unlock(struct mrf24j40 *devrec) { - u8 len = RX_FIFO_SIZE; - u8 lqi = 0; - u8 val; - int ret = 0; - int ret2; - struct sk_buff *skb; + int ret; - /* Turn off reception of packets off the air. This prevents the - * device from overwriting the buffer while we're reading it. */ - ret = read_short_reg(devrec, REG_BBREG1, &val); + /* Turn back on reception of packets off the air. */ + devrec->rx_msg.complete = NULL; + devrec->rx_buf[0] = MRF24J40_WRITESHORT(REG_BBREG1); + devrec->rx_buf[1] = 0x00; /* CLR RXDECINV */ + ret = spi_async(devrec->spi, &devrec->rx_msg); if (ret) - goto out; - val |= 4; /* SET RXDECINV */ - write_short_reg(devrec, REG_BBREG1, val); + dev_err(printdev(devrec), "failed to unlock rx buffer\n"); +} - skb = dev_alloc_skb(len); +static void mrf24j40_handle_rx_read_buf_complete(void *context) +{ + struct mrf24j40 *devrec = context; + u8 len = devrec->rx_buf[2]; + u8 rx_local_buf[RX_FIFO_SIZE]; + struct sk_buff *skb; + + memcpy(rx_local_buf, devrec->rx_fifo_buf, len); + mrf24j40_handle_rx_read_buf_unlock(devrec); + + skb = dev_alloc_skb(IEEE802154_MTU); if (!skb) { - ret = -ENOMEM; - goto out; + dev_err(printdev(devrec), "failed to allocate skb\n"); + return; } - ret = mrf24j40_read_rx_buf(devrec, skb_put(skb, len), &len, &lqi); - if (ret < 0) { - dev_err(printdev(devrec), "Failure reading RX FIFO\n"); - kfree_skb(skb); - ret = -EINVAL; - goto out; + memcpy(skb_put(skb, len), rx_local_buf, len); + ieee802154_rx_irqsafe(devrec->hw, skb, 0); + +#ifdef DEBUG + print_hex_dump(KERN_DEBUG, "mrf24j40 rx: ", DUMP_PREFIX_OFFSET, 16, 1, + rx_local_buf, len, 0); + pr_debug("mrf24j40 rx: lqi: %02hhx rssi: %02hhx\n", + devrec->rx_lqi_buf[0], devrec->rx_lqi_buf[1]); +#endif +} + +static void mrf24j40_handle_rx_read_buf(void *context) +{ + struct mrf24j40 *devrec = context; + u16 cmd; + int ret; + + /* if length is invalid read the full MTU */ + if (!ieee802154_is_valid_psdu_len(devrec->rx_buf[2])) + devrec->rx_buf[2] = IEEE802154_MTU; + + cmd = MRF24J40_READLONG(REG_RX_FIFO + 1); + devrec->rx_addr_buf[0] = cmd >> 8 & 0xff; + devrec->rx_addr_buf[1] = cmd & 0xff; + devrec->rx_fifo_buf_trx.len = devrec->rx_buf[2]; + ret = spi_async(devrec->spi, &devrec->rx_buf_msg); + if (ret) { + dev_err(printdev(devrec), "failed to read rx buffer\n"); + mrf24j40_handle_rx_read_buf_unlock(devrec); } +} - /* TODO: Other drivers call ieee20154_rx_irqsafe() here (eg: cc2040, - * also from a workqueue). I think irqsafe is not necessary here. - * Can someone confirm? */ - ieee802154_rx_irqsafe(devrec->hw, skb, lqi); +static void mrf24j40_handle_rx_read_len(void *context) +{ + struct mrf24j40 *devrec = context; + u16 cmd; + int ret; - dev_dbg(printdev(devrec), "RX Handled\n"); + /* read the length of received frame */ + devrec->rx_msg.complete = mrf24j40_handle_rx_read_buf; + devrec->rx_trx.len = 3; + cmd = MRF24J40_READLONG(REG_RX_FIFO); + devrec->rx_buf[0] = cmd >> 8 & 0xff; + devrec->rx_buf[1] = cmd & 0xff; -out: - /* Turn back on reception of packets off the air. */ - ret2 = read_short_reg(devrec, REG_BBREG1, &val); - if (ret2) - return ret2; - val &= ~0x4; /* Clear RXDECINV */ - write_short_reg(devrec, REG_BBREG1, val); + ret = spi_async(devrec->spi, &devrec->rx_msg); + if (ret) { + dev_err(printdev(devrec), "failed to read rx buffer length\n"); + mrf24j40_handle_rx_read_buf_unlock(devrec); + } +} - return ret; +static int mrf24j40_handle_rx(struct mrf24j40 *devrec) +{ + /* Turn off reception of packets off the air. This prevents the + * device from overwriting the buffer while we're reading it. + */ + devrec->rx_msg.complete = mrf24j40_handle_rx_read_len; + devrec->rx_trx.len = 2; + devrec->rx_buf[0] = MRF24J40_WRITESHORT(REG_BBREG1); + devrec->rx_buf[1] = 0x04; /* SET RXDECINV */ + + return spi_async(devrec->spi, &devrec->rx_msg); } static const struct ieee802154_ops mrf24j40_ops = { @@ -1025,6 +955,29 @@ mrf24j40_setup_tx_spi_messages(struct mrf24j40 *devrec) spi_message_add_tail(&devrec->tx_post_trx, &devrec->tx_post_msg); } +static void +mrf24j40_setup_rx_spi_messages(struct mrf24j40 *devrec) +{ + spi_message_init(&devrec->rx_msg); + devrec->rx_msg.context = devrec; + devrec->rx_trx.len = 2; + devrec->rx_trx.tx_buf = devrec->rx_buf; + devrec->rx_trx.rx_buf = devrec->rx_buf; + spi_message_add_tail(&devrec->rx_trx, &devrec->rx_msg); + + spi_message_init(&devrec->rx_buf_msg); + devrec->rx_buf_msg.context = devrec; + devrec->rx_buf_msg.complete = mrf24j40_handle_rx_read_buf_complete; + devrec->rx_addr_trx.len = 2; + devrec->rx_addr_trx.tx_buf = devrec->rx_addr_buf; + spi_message_add_tail(&devrec->rx_addr_trx, &devrec->rx_buf_msg); + devrec->rx_fifo_buf_trx.rx_buf = devrec->rx_fifo_buf; + spi_message_add_tail(&devrec->rx_fifo_buf_trx, &devrec->rx_buf_msg); + devrec->rx_lqi_trx.len = 2; + devrec->rx_lqi_trx.rx_buf = devrec->rx_lqi_buf; + spi_message_add_tail(&devrec->rx_lqi_trx, &devrec->rx_buf_msg); +} + static void mrf24j40_phy_setup(struct mrf24j40 *devrec) { ieee802154_random_extended_addr(&devrec->hw->phy->perm_extended_addr); @@ -1054,6 +1007,7 @@ static int mrf24j40_probe(struct spi_device *spi) devrec->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT; mrf24j40_setup_tx_spi_messages(devrec); + mrf24j40_setup_rx_spi_messages(devrec); devrec->regmap_short = devm_regmap_init_spi(spi, &mrf24j40_short_regmap); -- cgit v0.10.2 From 374416112bb0f83c5b4d8bb3fbb157fbe0b2814b Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 21 Sep 2015 11:24:36 +0200 Subject: mrf24j40: async interrupt handling This patch removes the threaded irq handling and do a hardirq instead. We need to switch to spi_async for this step for getting the irq status register. Reviewed-by: Stefan Schmidt Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index 0cb251e..940fc0a 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -181,8 +181,10 @@ struct mrf24j40 { u8 rx_fifo_buf[RX_FIFO_SIZE]; struct spi_transfer rx_fifo_buf_trx; - struct mutex buffer_mutex; /* only used to protect buf */ - u8 *buf; /* 3 bytes. Used for SPI single-register transfers. */ + /* isr handling for reading intstat */ + struct spi_message irq_msg; + u8 irq_buf[2]; + struct spi_transfer irq_trx; }; /* regmap information for short address register access */ @@ -486,34 +488,6 @@ static const struct regmap_bus mrf24j40_long_regmap_bus = { .val_format_endian_default = REGMAP_ENDIAN_BIG, }; -static int read_short_reg(struct mrf24j40 *devrec, u8 reg, u8 *val) -{ - int ret = -1; - struct spi_message msg; - struct spi_transfer xfer = { - .len = 2, - .tx_buf = devrec->buf, - .rx_buf = devrec->buf, - }; - - spi_message_init(&msg); - spi_message_add_tail(&xfer, &msg); - - mutex_lock(&devrec->buffer_mutex); - devrec->buf[0] = MRF24J40_READSHORT(reg); - devrec->buf[1] = 0; - - ret = spi_sync(devrec->spi, &msg); - if (ret) - dev_err(printdev(devrec), - "SPI read Failed for short register 0x%hhx\n", reg); - else - *val = devrec->buf[1]; - - mutex_unlock(&devrec->buffer_mutex); - return ret; -} - static void write_tx_buf_complete(void *context) { struct mrf24j40 *devrec = context; @@ -812,16 +786,12 @@ static const struct ieee802154_ops mrf24j40_ops = { .set_hw_addr_filt = mrf24j40_filter, }; -static irqreturn_t mrf24j40_isr(int irq, void *data) +static void mrf24j40_intstat_complete(void *context) { - struct mrf24j40 *devrec = data; - u8 intstat; - int ret; + struct mrf24j40 *devrec = context; + u8 intstat = devrec->irq_buf[1]; - /* Read the interrupt status */ - ret = read_short_reg(devrec, REG_INTSTAT, &intstat); - if (ret) - goto out; + enable_irq(devrec->spi->irq); /* Check for TX complete */ if (intstat & 0x1) @@ -830,8 +800,23 @@ static irqreturn_t mrf24j40_isr(int irq, void *data) /* Check for Rx */ if (intstat & 0x8) mrf24j40_handle_rx(devrec); +} + +static irqreturn_t mrf24j40_isr(int irq, void *data) +{ + struct mrf24j40 *devrec = data; + int ret; + + disable_irq_nosync(irq); + + devrec->irq_buf[0] = MRF24J40_READSHORT(REG_INTSTAT); + /* Read the interrupt status */ + ret = spi_async(devrec->spi, &devrec->irq_msg); + if (ret) { + enable_irq(irq); + return IRQ_NONE; + } -out: return IRQ_HANDLED; } @@ -978,6 +963,18 @@ mrf24j40_setup_rx_spi_messages(struct mrf24j40 *devrec) spi_message_add_tail(&devrec->rx_lqi_trx, &devrec->rx_buf_msg); } +static void +mrf24j40_setup_irq_spi_messages(struct mrf24j40 *devrec) +{ + spi_message_init(&devrec->irq_msg); + devrec->irq_msg.context = devrec; + devrec->irq_msg.complete = mrf24j40_intstat_complete; + devrec->irq_trx.len = 2; + devrec->irq_trx.tx_buf = devrec->irq_buf; + devrec->irq_trx.rx_buf = devrec->irq_buf; + spi_message_add_tail(&devrec->irq_trx, &devrec->irq_msg); +} + static void mrf24j40_phy_setup(struct mrf24j40 *devrec) { ieee802154_random_extended_addr(&devrec->hw->phy->perm_extended_addr); @@ -1008,6 +1005,7 @@ static int mrf24j40_probe(struct spi_device *spi) mrf24j40_setup_tx_spi_messages(devrec); mrf24j40_setup_rx_spi_messages(devrec); + mrf24j40_setup_irq_spi_messages(devrec); devrec->regmap_short = devm_regmap_init_spi(spi, &mrf24j40_short_regmap); @@ -1028,32 +1026,21 @@ static int mrf24j40_probe(struct spi_device *spi) goto err_register_device; } - devrec->buf = devm_kzalloc(&spi->dev, 3, GFP_KERNEL); - if (!devrec->buf) - goto err_register_device; - if (spi->max_speed_hz > MAX_SPI_SPEED_HZ) { dev_warn(&spi->dev, "spi clock above possible maximum: %d", MAX_SPI_SPEED_HZ); return -EINVAL; } - mutex_init(&devrec->buffer_mutex); - ret = mrf24j40_hw_init(devrec); if (ret) goto err_register_device; mrf24j40_phy_setup(devrec); - ret = devm_request_threaded_irq(&spi->dev, - spi->irq, - NULL, - mrf24j40_isr, - IRQF_TRIGGER_LOW|IRQF_ONESHOT, - dev_name(&spi->dev), - devrec); - + ret = devm_request_irq(&spi->dev, spi->irq, mrf24j40_isr, + IRQF_TRIGGER_LOW, dev_name(&spi->dev), + devrec); if (ret) { dev_err(printdev(devrec), "Unable to get IRQ"); goto err_register_device; -- cgit v0.10.2 From 2323cf38f949439bc4ac4252cf1d91d1297cce92 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 21 Sep 2015 11:24:37 +0200 Subject: mrf24j40: add csma params support This patch adds supports to change the CSMA parameters. The datasheet doesn't say anything about max_be value. Seems not configurable and we assume the 802.15.4 default. But this value must exists because there is a min_be value. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index 940fc0a..fe7991d 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -776,6 +776,21 @@ static int mrf24j40_handle_rx(struct mrf24j40 *devrec) return spi_async(devrec->spi, &devrec->rx_msg); } +static int +mrf24j40_csma_params(struct ieee802154_hw *hw, u8 min_be, u8 max_be, + u8 retries) +{ + struct mrf24j40 *devrec = hw->priv; + u8 val; + + /* min_be */ + val = min_be << 3; + /* csma backoffs */ + val |= retries; + + return regmap_update_bits(devrec->regmap_short, REG_TXMCR, 0x1f, val); +} + static const struct ieee802154_ops mrf24j40_ops = { .owner = THIS_MODULE, .xmit_async = mrf24j40_tx, @@ -784,6 +799,7 @@ static const struct ieee802154_ops mrf24j40_ops = { .stop = mrf24j40_stop, .set_channel = mrf24j40_set_channel, .set_hw_addr_filt = mrf24j40_filter, + .set_csma_params = mrf24j40_csma_params, }; static void mrf24j40_intstat_complete(void *context) @@ -979,6 +995,14 @@ static void mrf24j40_phy_setup(struct mrf24j40 *devrec) { ieee802154_random_extended_addr(&devrec->hw->phy->perm_extended_addr); devrec->hw->phy->current_channel = 11; + + /* mrf24j40 supports max_minbe 0 - 3 */ + devrec->hw->phy->supported.max_minbe = 3; + /* datasheet doesn't say anything about max_be, but we have min_be + * So we assume the max_be default. + */ + devrec->hw->phy->supported.min_maxbe = 5; + devrec->hw->phy->supported.max_maxbe = 5; } static int mrf24j40_probe(struct spi_device *spi) @@ -1001,7 +1025,8 @@ static int mrf24j40_probe(struct spi_device *spi) devrec->hw = hw; devrec->hw->parent = &spi->dev; devrec->hw->phy->supported.channels[0] = CHANNEL_MASK; - devrec->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT; + devrec->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT | + IEEE802154_HW_CSMA_PARAMS; mrf24j40_setup_tx_spi_messages(devrec); mrf24j40_setup_rx_spi_messages(devrec); -- cgit v0.10.2 From f1d781272231dc4d1cdfdce13ad3e6f3f96b7ec0 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 21 Sep 2015 11:24:38 +0200 Subject: mrf24j40: add cca mode support This patch supports cca mode handling for mrf24j40. Reviewed-by: Stefan Schmidt Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index fe7991d..086eee0 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -791,6 +791,37 @@ mrf24j40_csma_params(struct ieee802154_hw *hw, u8 min_be, u8 max_be, return regmap_update_bits(devrec->regmap_short, REG_TXMCR, 0x1f, val); } +static int mrf24j40_set_cca_mode(struct ieee802154_hw *hw, + const struct wpan_phy_cca *cca) +{ + struct mrf24j40 *devrec = hw->priv; + u8 val; + + /* mapping 802.15.4 to driver spec */ + switch (cca->mode) { + case NL802154_CCA_ENERGY: + val = 2; + break; + case NL802154_CCA_CARRIER: + val = 1; + break; + case NL802154_CCA_ENERGY_CARRIER: + switch (cca->opt) { + case NL802154_CCA_OPT_ENERGY_CARRIER_AND: + val = 3; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + return regmap_update_bits(devrec->regmap_short, REG_BBREG2, 0xc0, + val << 6); +} + static const struct ieee802154_ops mrf24j40_ops = { .owner = THIS_MODULE, .xmit_async = mrf24j40_tx, @@ -800,6 +831,7 @@ static const struct ieee802154_ops mrf24j40_ops = { .set_channel = mrf24j40_set_channel, .set_hw_addr_filt = mrf24j40_filter, .set_csma_params = mrf24j40_csma_params, + .set_cca_mode = mrf24j40_set_cca_mode, }; static void mrf24j40_intstat_complete(void *context) @@ -1003,6 +1035,12 @@ static void mrf24j40_phy_setup(struct mrf24j40 *devrec) */ devrec->hw->phy->supported.min_maxbe = 5; devrec->hw->phy->supported.max_maxbe = 5; + + devrec->hw->phy->cca.mode = NL802154_CCA_CARRIER;; + devrec->hw->phy->supported.cca_modes = BIT(NL802154_CCA_ENERGY) | + BIT(NL802154_CCA_CARRIER) | + BIT(NL802154_CCA_ENERGY_CARRIER); + devrec->hw->phy->supported.cca_opts = BIT(NL802154_CCA_OPT_ENERGY_CARRIER_AND); } static int mrf24j40_probe(struct spi_device *spi) @@ -1028,6 +1066,8 @@ static int mrf24j40_probe(struct spi_device *spi) devrec->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT | IEEE802154_HW_CSMA_PARAMS; + devrec->hw->phy->flags = WPAN_PHY_FLAG_CCA_MODE; + mrf24j40_setup_tx_spi_messages(devrec); mrf24j40_setup_rx_spi_messages(devrec); mrf24j40_setup_irq_spi_messages(devrec); -- cgit v0.10.2 From e33a0f96ac518d4c7ffcf98d2f030e6e23fab6b3 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 21 Sep 2015 11:24:39 +0200 Subject: mrf24j40: add cca ed level support This patch supports handling to set the cca energy detection level for the mrf24j40 transceiver. Reviewed-by: Stefan Schmidt Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index 086eee0..c23936e 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -822,6 +822,48 @@ static int mrf24j40_set_cca_mode(struct ieee802154_hw *hw, val << 6); } +/* array for representing ed levels */ +static const s32 mrf24j40_ed_levels[] = { + -9000, -8900, -8800, -8700, -8600, -8500, -8400, -8300, -8200, -8100, + -8000, -7900, -7800, -7700, -7600, -7500, -7400, -7300, -7200, -7100, + -7000, -6900, -6800, -6700, -6600, -6500, -6400, -6300, -6200, -6100, + -6000, -5900, -5800, -5700, -5600, -5500, -5400, -5300, -5200, -5100, + -5000, -4900, -4800, -4700, -4600, -4500, -4400, -4300, -4200, -4100, + -4000, -3900, -3800, -3700, -3600, -3500 +}; + +/* map ed levels to register value */ +static const s32 mrf24j40_ed_levels_map[][2] = { + { -9000, 0 }, { -8900, 1 }, { -8800, 2 }, { -8700, 5 }, { -8600, 9 }, + { -8500, 13 }, { -8400, 18 }, { -8300, 23 }, { -8200, 27 }, + { -8100, 32 }, { -8000, 37 }, { -7900, 43 }, { -7800, 48 }, + { -7700, 53 }, { -7600, 58 }, { -7500, 63 }, { -7400, 68 }, + { -7300, 73 }, { -7200, 78 }, { -7100, 83 }, { -7000, 89 }, + { -6900, 95 }, { -6800, 100 }, { -6700, 107 }, { -6600, 111 }, + { -6500, 117 }, { -6400, 121 }, { -6300, 125 }, { -6200, 129 }, + { -6100, 133 }, { -6000, 138 }, { -5900, 143 }, { -5800, 148 }, + { -5700, 153 }, { -5600, 159 }, { -5500, 165 }, { -5400, 170 }, + { -5300, 176 }, { -5200, 183 }, { -5100, 188 }, { -5000, 193 }, + { -4900, 198 }, { -4800, 203 }, { -4700, 207 }, { -4600, 212 }, + { -4500, 216 }, { -4400, 221 }, { -4300, 225 }, { -4200, 228 }, + { -4100, 233 }, { -4000, 239 }, { -3900, 245 }, { -3800, 250 }, + { -3700, 253 }, { -3600, 254 }, { -3500, 255 }, +}; + +static int mrf24j40_set_cca_ed_level(struct ieee802154_hw *hw, s32 mbm) +{ + struct mrf24j40 *devrec = hw->priv; + int i; + + for (i = 0; i < ARRAY_SIZE(mrf24j40_ed_levels_map); i++) { + if (mrf24j40_ed_levels_map[i][0] == mbm) + return regmap_write(devrec->regmap_short, REG_CCAEDTH, + mrf24j40_ed_levels_map[i][1]); + } + + return -EINVAL; +} + static const struct ieee802154_ops mrf24j40_ops = { .owner = THIS_MODULE, .xmit_async = mrf24j40_tx, @@ -832,6 +874,7 @@ static const struct ieee802154_ops mrf24j40_ops = { .set_hw_addr_filt = mrf24j40_filter, .set_csma_params = mrf24j40_csma_params, .set_cca_mode = mrf24j40_set_cca_mode, + .set_cca_ed_level = mrf24j40_set_cca_ed_level, }; static void mrf24j40_intstat_complete(void *context) @@ -1041,6 +1084,10 @@ static void mrf24j40_phy_setup(struct mrf24j40 *devrec) BIT(NL802154_CCA_CARRIER) | BIT(NL802154_CCA_ENERGY_CARRIER); devrec->hw->phy->supported.cca_opts = BIT(NL802154_CCA_OPT_ENERGY_CARRIER_AND); + + devrec->hw->phy->cca_ed_level = -6900; + devrec->hw->phy->supported.cca_ed_levels = mrf24j40_ed_levels; + devrec->hw->phy->supported.cca_ed_levels_size = ARRAY_SIZE(mrf24j40_ed_levels); } static int mrf24j40_probe(struct spi_device *spi) @@ -1066,7 +1113,8 @@ static int mrf24j40_probe(struct spi_device *spi) devrec->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT | IEEE802154_HW_CSMA_PARAMS; - devrec->hw->phy->flags = WPAN_PHY_FLAG_CCA_MODE; + devrec->hw->phy->flags = WPAN_PHY_FLAG_CCA_MODE | + WPAN_PHY_FLAG_CCA_ED_LEVEL; mrf24j40_setup_tx_spi_messages(devrec); mrf24j40_setup_rx_spi_messages(devrec); -- cgit v0.10.2 From 00250f78896bc52fd4606c4de7e3fb174e702123 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 21 Sep 2015 11:24:40 +0200 Subject: mrf24j40: add tx power support This patch supports setting of transmit power for the mrf24j40ma transceiver only. The mrf24j40mc has some amplifier to change the transmit power, I am currently not sure how the mapping for this amplifier looks like. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index c23936e..91687c1 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -864,6 +864,65 @@ static int mrf24j40_set_cca_ed_level(struct ieee802154_hw *hw, s32 mbm) return -EINVAL; } +static const s32 mrf24j40ma_powers[] = { + 0, -50, -120, -190, -280, -370, -490, -630, -1000, -1050, -1120, -1190, + -1280, -1370, -1490, -1630, -2000, -2050, -2120, -2190, -2280, -2370, + -2490, -2630, -3000, -3050, -3120, -3190, -3280, -3370, -3490, -3630, +}; + +static int mrf24j40_set_txpower(struct ieee802154_hw *hw, s32 mbm) +{ + struct mrf24j40 *devrec = hw->priv; + s32 small_scale; + u8 val; + + if (0 >= mbm && mbm > -1000) { + val = 0; + small_scale = mbm; + } else if (-1000 >= mbm && mbm > -2000) { + val = 0x40; + small_scale = mbm + 1000; + } else if (-2000 >= mbm && mbm > -3000) { + val = 0x80; + small_scale = mbm + 2000; + } else if (-3000 >= mbm && mbm > -4000) { + val = 0xc0; + small_scale = mbm + 3000; + } else { + return -EINVAL; + } + + switch (small_scale) { + case 0: + break; + case -50: + val |= 0x08; + break; + case -120: + val |= 0x10; + break; + case -190: + val |= 0x18; + break; + case -280: + val |= 0x20; + break; + case -370: + val |= 0x28; + break; + case -490: + val |= 0x30; + break; + case -630: + val |= 0x38; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(devrec->regmap_long, REG_RFCON3, 0xf8, val); +} + static const struct ieee802154_ops mrf24j40_ops = { .owner = THIS_MODULE, .xmit_async = mrf24j40_tx, @@ -875,6 +934,7 @@ static const struct ieee802154_ops mrf24j40_ops = { .set_csma_params = mrf24j40_csma_params, .set_cca_mode = mrf24j40_set_cca_mode, .set_cca_ed_level = mrf24j40_set_cca_ed_level, + .set_txpower = mrf24j40_set_txpower, }; static void mrf24j40_intstat_complete(void *context) @@ -1088,6 +1148,17 @@ static void mrf24j40_phy_setup(struct mrf24j40 *devrec) devrec->hw->phy->cca_ed_level = -6900; devrec->hw->phy->supported.cca_ed_levels = mrf24j40_ed_levels; devrec->hw->phy->supported.cca_ed_levels_size = ARRAY_SIZE(mrf24j40_ed_levels); + + switch (spi_get_device_id(devrec->spi)->driver_data) { + case MRF24J40: + case MRF24J40MA: + devrec->hw->phy->supported.tx_powers = mrf24j40ma_powers; + devrec->hw->phy->supported.tx_powers_size = ARRAY_SIZE(mrf24j40ma_powers); + devrec->hw->phy->flags |= WPAN_PHY_FLAG_TXPOWER; + break; + default: + break; + } } static int mrf24j40_probe(struct spi_device *spi) -- cgit v0.10.2 From 8ba40417401ce771769d847900f08c978bb61632 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 21 Sep 2015 11:24:41 +0200 Subject: mrf24j40: add promiscuous mode support This patch adds support for promiscuous mode by setting promiscuous (no frame filtering), disable automatic ack handling and not filtering frames where the crc is invalid. Reviewed-by: Stefan Schmidt Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index 91687c1..146ad57 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -923,6 +923,24 @@ static int mrf24j40_set_txpower(struct ieee802154_hw *hw, s32 mbm) return regmap_update_bits(devrec->regmap_long, REG_RFCON3, 0xf8, val); } +static int mrf24j40_set_promiscuous_mode(struct ieee802154_hw *hw, bool on) +{ + struct mrf24j40 *devrec = hw->priv; + int ret; + + if (on) { + /* set PROMI, ERRPKT and NOACKRSP */ + ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR, 0x23, + 0x23); + } else { + /* clear PROMI, ERRPKT and NOACKRSP */ + ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR, 0x23, + 0x00); + } + + return ret; +} + static const struct ieee802154_ops mrf24j40_ops = { .owner = THIS_MODULE, .xmit_async = mrf24j40_tx, @@ -935,6 +953,7 @@ static const struct ieee802154_ops mrf24j40_ops = { .set_cca_mode = mrf24j40_set_cca_mode, .set_cca_ed_level = mrf24j40_set_cca_ed_level, .set_txpower = mrf24j40_set_txpower, + .set_promiscuous_mode = mrf24j40_set_promiscuous_mode, }; static void mrf24j40_intstat_complete(void *context) @@ -1182,7 +1201,8 @@ static int mrf24j40_probe(struct spi_device *spi) devrec->hw->parent = &spi->dev; devrec->hw->phy->supported.channels[0] = CHANNEL_MASK; devrec->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT | - IEEE802154_HW_CSMA_PARAMS; + IEEE802154_HW_CSMA_PARAMS | + IEEE802154_HW_PROMISCUOUS; devrec->hw->phy->flags = WPAN_PHY_FLAG_CCA_MODE | WPAN_PHY_FLAG_CCA_ED_LEVEL; -- cgit v0.10.2 From afaf7fdedb6cc484b4eccf48227632c7d81dcf5a Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 21 Sep 2015 11:24:42 +0200 Subject: mrf24j40: change irq trigger type behaviour This patch changes the irq trigger type value while calling devm_request_irq by using IRQF_TRIGGER_LOW when no irq type was given. Additional we add support for change the irq polarity while hw init if high level or low level triggered irq type are given. For rising edge triggered irq's the mrf24j40 can't deal with that, this races at position of tx completion irq, while the irq is disabled we readout the irq status registers. This will resets the irq line so other irq's can occur. Wile readout the irq status register the irq is still disabled and edge triggered interrupts will be ignored. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index 146ad57..9a8d517 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -992,6 +993,7 @@ static irqreturn_t mrf24j40_isr(int irq, void *data) static int mrf24j40_hw_init(struct mrf24j40 *devrec) { + u32 irq_type; int ret; /* Initialize the device. @@ -1083,6 +1085,25 @@ static int mrf24j40_hw_init(struct mrf24j40 *devrec) regmap_write(devrec->regmap_long, REG_RFCON3, 0x28); } + irq_type = irq_get_trigger_type(devrec->spi->irq); + if (irq_type == IRQ_TYPE_EDGE_RISING || + irq_type == IRQ_TYPE_EDGE_FALLING) + dev_warn(&devrec->spi->dev, + "Using edge triggered irq's are not recommended, because it can cause races and result in a non-functional driver!\n"); + switch (irq_type) { + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_LEVEL_HIGH: + /* set interrupt polarity to rising */ + ret = regmap_update_bits(devrec->regmap_long, REG_SLPCON0, + 0x02, 0x02); + if (ret) + goto err_ret; + break; + default: + /* default is falling edge */ + break; + } + return 0; err_ret: @@ -1182,7 +1203,7 @@ static void mrf24j40_phy_setup(struct mrf24j40 *devrec) static int mrf24j40_probe(struct spi_device *spi) { - int ret = -ENOMEM; + int ret = -ENOMEM, irq_type; struct ieee802154_hw *hw; struct mrf24j40 *devrec; @@ -1242,9 +1263,13 @@ static int mrf24j40_probe(struct spi_device *spi) mrf24j40_phy_setup(devrec); + /* request IRQF_TRIGGER_LOW as fallback default */ + irq_type = irq_get_trigger_type(spi->irq); + if (!irq_type) + irq_type = IRQF_TRIGGER_LOW; + ret = devm_request_irq(&spi->dev, spi->irq, mrf24j40_isr, - IRQF_TRIGGER_LOW, dev_name(&spi->dev), - devrec); + irq_type, dev_name(&spi->dev), devrec); if (ret) { dev_err(printdev(devrec), "Unable to get IRQ"); goto err_register_device; -- cgit v0.10.2 From 7d840545e5b933028e7423b127459d0d5e5e04fb Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 21 Sep 2015 11:24:43 +0200 Subject: mrf24j40: replace magic numbers This patch replaces some magic numbers with defines for register bits, mask and shifts. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index 9a8d517..2770df1 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -26,6 +26,11 @@ /* MRF24J40 Short Address Registers */ #define REG_RXMCR 0x00 /* Receive MAC control */ +#define BIT_PROMI BIT(0) +#define BIT_ERRPKT BIT(1) +#define BIT_NOACKRSP BIT(5) +#define BIT_PANCOORD BIT(3) + #define REG_PANIDL 0x01 /* PAN ID (low) */ #define REG_PANIDH 0x02 /* PAN ID (high) */ #define REG_SADRL 0x03 /* Short address (low) */ @@ -41,6 +46,11 @@ #define REG_RXFLUSH 0x0D #define REG_ORDER 0x10 #define REG_TXMCR 0x11 /* Transmit MAC control */ +#define TXMCR_MIN_BE_SHIFT 3 +#define TXMCR_MIN_BE_MASK 0x18 +#define TXMCR_CSMA_RETRIES_SHIFT 0 +#define TXMCR_CSMA_RETRIES_MASK 0x07 + #define REG_ACKTMOUT 0x12 #define REG_ESLOTG1 0x13 #define REG_SYMTICKL 0x14 @@ -50,6 +60,9 @@ #define REG_PACON2 0x18 /* Power Amplifier Control */ #define REG_TXBCON0 0x1A #define REG_TXNCON 0x1B /* Transmit Normal FIFO Control */ +#define BIT_TXNTRIG BIT(0) +#define BIT_TXNACKREQ BIT(2) + #define REG_TXG1CON 0x1C #define REG_TXG2CON 0x1D #define REG_ESLOTG23 0x1E @@ -70,15 +83,28 @@ #define REG_TXSTBL 0x2E /* TX Stabilization */ #define REG_RXSR 0x30 #define REG_INTSTAT 0x31 /* Interrupt Status */ +#define BIT_TXNIF BIT(0) +#define BIT_RXIF BIT(3) + #define REG_INTCON 0x32 /* Interrupt Control */ +#define BIT_TXNIE BIT(0) +#define BIT_RXIE BIT(3) + #define REG_GPIO 0x33 /* GPIO */ #define REG_TRISGPIO 0x34 /* GPIO direction */ #define REG_SLPACK 0x35 #define REG_RFCTL 0x36 /* RF Control Mode Register */ +#define BIT_RFRST BIT(2) + #define REG_SECCR2 0x37 #define REG_BBREG0 0x38 #define REG_BBREG1 0x39 /* Baseband Registers */ +#define BIT_RXDECINV BIT(2) + #define REG_BBREG2 0x3A /* */ +#define BBREG2_CCA_MODE_SHIFT 6 +#define BBREG2_CCA_MODE_MASK 0xc0 + #define REG_BBREG3 0x3B #define REG_BBREG4 0x3C #define REG_BBREG6 0x3E /* */ @@ -86,9 +112,32 @@ /* MRF24J40 Long Address Registers */ #define REG_RFCON0 0x200 /* RF Control Registers */ +#define RFCON0_CH_SHIFT 4 +#define RFCON0_CH_MASK 0xf0 +#define RFOPT_RECOMMEND 3 + #define REG_RFCON1 0x201 #define REG_RFCON2 0x202 #define REG_RFCON3 0x203 + +#define TXPWRL_MASK 0xc0 +#define TXPWRL_SHIFT 6 +#define TXPWRL_30 0x3 +#define TXPWRL_20 0x2 +#define TXPWRL_10 0x1 +#define TXPWRL_0 0x0 + +#define TXPWRS_MASK 0x38 +#define TXPWRS_SHIFT 3 +#define TXPWRS_6_3 0x7 +#define TXPWRS_4_9 0x6 +#define TXPWRS_3_7 0x5 +#define TXPWRS_2_8 0x4 +#define TXPWRS_1_9 0x3 +#define TXPWRS_1_2 0x2 +#define TXPWRS_0_5 0x1 +#define TXPWRS_0 0x0 + #define REG_RFCON5 0x205 #define REG_RFCON6 0x206 #define REG_RFCON7 0x207 @@ -99,6 +148,8 @@ #define REG_RFSTATE 0x20F #define REG_RSSI 0x210 #define REG_SLPCON0 0x211 /* Sleep Clock Control Registers */ +#define BIT_INTEDGE BIT(1) + #define REG_SLPCON1 0x220 #define REG_WAKETIMEL 0x222 /* Wake-up Time Match Value Low */ #define REG_WAKETIMEH 0x223 /* Wake-up Time Match Value High */ @@ -493,11 +544,11 @@ static void write_tx_buf_complete(void *context) { struct mrf24j40 *devrec = context; __le16 fc = ieee802154_get_fc_from_skb(devrec->tx_skb); - u8 val = 0x01; + u8 val = BIT_TXNTRIG; int ret; if (ieee802154_is_ackreq(fc)) - val |= 0x04; + val |= BIT_TXNACKREQ; devrec->tx_post_msg.complete = NULL; devrec->tx_post_buf[0] = MRF24J40_WRITESHORT(REG_TXNCON); @@ -564,7 +615,7 @@ static int mrf24j40_start(struct ieee802154_hw *hw) /* Clear TXNIE and RXIE. Enable interrupts */ return regmap_update_bits(devrec->regmap_short, REG_INTCON, - 0x01 | 0x08, 0x00); + BIT_TXNIE | BIT_RXIE, 0); } static void mrf24j40_stop(struct ieee802154_hw *hw) @@ -574,8 +625,8 @@ static void mrf24j40_stop(struct ieee802154_hw *hw) dev_dbg(printdev(devrec), "stop\n"); /* Set TXNIE and RXIE. Disable Interrupts */ - regmap_update_bits(devrec->regmap_short, REG_INTCON, 0x01 | 0x08, - 0x01 | 0x08); + regmap_update_bits(devrec->regmap_short, REG_INTCON, + BIT_TXNIE | BIT_TXNIE, BIT_TXNIE | BIT_TXNIE); } static int mrf24j40_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel) @@ -591,17 +642,19 @@ static int mrf24j40_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel) WARN_ON(channel > MRF24J40_CHAN_MAX); /* Set Channel TODO */ - val = (channel-11) << 4 | 0x03; - ret = regmap_update_bits(devrec->regmap_long, REG_RFCON0, 0xf0, val); + val = (channel - 11) << RFCON0_CH_SHIFT | RFOPT_RECOMMEND; + ret = regmap_update_bits(devrec->regmap_long, REG_RFCON0, + RFCON0_CH_MASK, val); if (ret) return ret; /* RF Reset */ - ret = regmap_update_bits(devrec->regmap_short, REG_RFCTL, 0x04, 0x04); + ret = regmap_update_bits(devrec->regmap_short, REG_RFCTL, BIT_RFRST, + BIT_RFRST); if (ret) return ret; - ret = regmap_update_bits(devrec->regmap_short, REG_RFCTL, 0x04, 0x00); + ret = regmap_update_bits(devrec->regmap_short, REG_RFCTL, BIT_RFRST, 0); if (!ret) udelay(SET_CHANNEL_DELAY_US); /* per datasheet */ @@ -664,11 +717,11 @@ static int mrf24j40_filter(struct ieee802154_hw *hw, int ret; if (filt->pan_coord) - val = 0x8; + val = BIT_PANCOORD; else - val = 0x0; - ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR, 0x8, - val); + val = 0; + ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR, + BIT_PANCOORD, val); if (ret) return ret; @@ -772,7 +825,7 @@ static int mrf24j40_handle_rx(struct mrf24j40 *devrec) devrec->rx_msg.complete = mrf24j40_handle_rx_read_len; devrec->rx_trx.len = 2; devrec->rx_buf[0] = MRF24J40_WRITESHORT(REG_BBREG1); - devrec->rx_buf[1] = 0x04; /* SET RXDECINV */ + devrec->rx_buf[1] = BIT_RXDECINV; /* SET RXDECINV */ return spi_async(devrec->spi, &devrec->rx_msg); } @@ -785,11 +838,13 @@ mrf24j40_csma_params(struct ieee802154_hw *hw, u8 min_be, u8 max_be, u8 val; /* min_be */ - val = min_be << 3; + val = min_be << TXMCR_MIN_BE_SHIFT; /* csma backoffs */ - val |= retries; + val |= retries << TXMCR_CSMA_RETRIES_SHIFT; - return regmap_update_bits(devrec->regmap_short, REG_TXMCR, 0x1f, val); + return regmap_update_bits(devrec->regmap_short, REG_TXMCR, + TXMCR_MIN_BE_MASK | TXMCR_CSMA_RETRIES_MASK, + val); } static int mrf24j40_set_cca_mode(struct ieee802154_hw *hw, @@ -819,8 +874,9 @@ static int mrf24j40_set_cca_mode(struct ieee802154_hw *hw, return -EINVAL; } - return regmap_update_bits(devrec->regmap_short, REG_BBREG2, 0xc0, - val << 6); + return regmap_update_bits(devrec->regmap_short, REG_BBREG2, + BBREG2_CCA_MODE_MASK, + val << BBREG2_CCA_MODE_SHIFT); } /* array for representing ed levels */ @@ -878,16 +934,16 @@ static int mrf24j40_set_txpower(struct ieee802154_hw *hw, s32 mbm) u8 val; if (0 >= mbm && mbm > -1000) { - val = 0; + val = TXPWRL_0 << TXPWRL_SHIFT; small_scale = mbm; } else if (-1000 >= mbm && mbm > -2000) { - val = 0x40; + val = TXPWRL_10 << TXPWRL_SHIFT; small_scale = mbm + 1000; } else if (-2000 >= mbm && mbm > -3000) { - val = 0x80; + val = TXPWRL_20 << TXPWRL_SHIFT; small_scale = mbm + 2000; } else if (-3000 >= mbm && mbm > -4000) { - val = 0xc0; + val = TXPWRL_30 << TXPWRL_SHIFT; small_scale = mbm + 3000; } else { return -EINVAL; @@ -895,33 +951,35 @@ static int mrf24j40_set_txpower(struct ieee802154_hw *hw, s32 mbm) switch (small_scale) { case 0: + val |= (TXPWRS_0 << TXPWRS_SHIFT); break; case -50: - val |= 0x08; + val |= (TXPWRS_0_5 << TXPWRS_SHIFT); break; case -120: - val |= 0x10; + val |= (TXPWRS_1_2 << TXPWRS_SHIFT); break; case -190: - val |= 0x18; + val |= (TXPWRS_1_9 << TXPWRS_SHIFT); break; case -280: - val |= 0x20; + val |= (TXPWRS_2_8 << TXPWRS_SHIFT); break; case -370: - val |= 0x28; + val |= (TXPWRS_3_7 << TXPWRS_SHIFT); break; case -490: - val |= 0x30; + val |= (TXPWRS_4_9 << TXPWRS_SHIFT); break; case -630: - val |= 0x38; + val |= (TXPWRS_6_3 << TXPWRS_SHIFT); break; default: return -EINVAL; } - return regmap_update_bits(devrec->regmap_long, REG_RFCON3, 0xf8, val); + return regmap_update_bits(devrec->regmap_long, REG_RFCON3, + TXPWRL_MASK | TXPWRS_MASK, val); } static int mrf24j40_set_promiscuous_mode(struct ieee802154_hw *hw, bool on) @@ -931,12 +989,14 @@ static int mrf24j40_set_promiscuous_mode(struct ieee802154_hw *hw, bool on) if (on) { /* set PROMI, ERRPKT and NOACKRSP */ - ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR, 0x23, - 0x23); + ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR, + BIT_PROMI | BIT_ERRPKT | BIT_NOACKRSP, + BIT_PROMI | BIT_ERRPKT | BIT_NOACKRSP); } else { /* clear PROMI, ERRPKT and NOACKRSP */ - ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR, 0x23, - 0x00); + ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR, + BIT_PROMI | BIT_ERRPKT | BIT_NOACKRSP, + 0); } return ret; @@ -965,11 +1025,11 @@ static void mrf24j40_intstat_complete(void *context) enable_irq(devrec->spi->irq); /* Check for TX complete */ - if (intstat & 0x1) + if (intstat & BIT_TXNIF) ieee802154_xmit_complete(devrec->hw, devrec->tx_skb, false); /* Check for Rx */ - if (intstat & 0x8) + if (intstat & BIT_RXIF) mrf24j40_handle_rx(devrec); } @@ -1095,7 +1155,7 @@ static int mrf24j40_hw_init(struct mrf24j40 *devrec) case IRQ_TYPE_LEVEL_HIGH: /* set interrupt polarity to rising */ ret = regmap_update_bits(devrec->regmap_long, REG_SLPCON0, - 0x02, 0x02); + BIT_INTEDGE, BIT_INTEDGE); if (ret) goto err_ret; break; -- cgit v0.10.2 From 53d6cfa581405d0aa47a5b9ceb027bc157fddebc Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 21 Sep 2015 03:06:41 -0700 Subject: Bluetooth: btmrvl: remove extra space in cast Coding style fix, extra spaces are removed to make casting consistent. Signed-off-by: Amitkumar Karwar Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index 5f7c9be..68b7ff0 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -194,22 +194,22 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = { static const struct sdio_device_id btmrvl_sdio_ids[] = { /* Marvell SD8688 Bluetooth device */ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9105), - .driver_data = (unsigned long) &btmrvl_sdio_sd8688 }, + .driver_data = (unsigned long)&btmrvl_sdio_sd8688 }, /* Marvell SD8787 Bluetooth device */ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911A), - .driver_data = (unsigned long) &btmrvl_sdio_sd8787 }, + .driver_data = (unsigned long)&btmrvl_sdio_sd8787 }, /* Marvell SD8787 Bluetooth AMP device */ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911B), - .driver_data = (unsigned long) &btmrvl_sdio_sd8787 }, + .driver_data = (unsigned long)&btmrvl_sdio_sd8787 }, /* Marvell SD8797 Bluetooth device */ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912A), - .driver_data = (unsigned long) &btmrvl_sdio_sd8797 }, + .driver_data = (unsigned long)&btmrvl_sdio_sd8797 }, /* Marvell SD8887 Bluetooth device */ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9136), .driver_data = (unsigned long)&btmrvl_sdio_sd8887 }, /* Marvell SD8897 Bluetooth device */ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912E), - .driver_data = (unsigned long) &btmrvl_sdio_sd8897 }, + .driver_data = (unsigned long)&btmrvl_sdio_sd8897 }, { } /* Terminating entry */ }; -- cgit v0.10.2 From f0ef67485f5918794143b7a85d60c1a52bc690ec Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 21 Sep 2015 03:06:42 -0700 Subject: Bluetooth: btmrvl: add sd8997 chipset support This patch adds support for Marvell's new chipset SD8997. Register offsets and supported feature flags are updated. Signed-off-by: Zhaoyang Liu Signed-off-by: Cathy Luo Signed-off-by: Amitkumar Karwar Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 0bd88c9..3d480d8 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -275,7 +275,7 @@ config BT_MRVL The core driver to support Marvell Bluetooth devices. This driver is required if you want to support - Marvell Bluetooth devices, such as 8688/8787/8797/8887/8897. + Marvell Bluetooth devices, such as 8688/8787/8797/8887/8897/8997. Say Y here to compile Marvell Bluetooth driver into the kernel or say M to compile it as module. @@ -289,7 +289,7 @@ config BT_MRVL_SDIO The driver for Marvell Bluetooth chipsets with SDIO interface. This driver is required if you want to use Marvell Bluetooth - devices with SDIO interface. Currently SD8688/SD8787/SD8797/SD8887/SD8897 + devices with SDIO interface. Currently SD8688/SD8787/SD8797/SD8887/SD8897/SD8997 chipsets are supported. Say Y here to compile support for Marvell BT-over-SDIO driver diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index 68b7ff0..7f6fb17 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -146,6 +146,29 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8897 = { .fw_dump_end = 0xea, }; +static const struct btmrvl_sdio_card_reg btmrvl_reg_8997 = { + .cfg = 0x00, + .host_int_mask = 0x08, + .host_intstatus = 0x0c, + .card_status = 0x5c, + .sq_read_base_addr_a0 = 0xf8, + .sq_read_base_addr_a1 = 0xf9, + .card_revision = 0xc8, + .card_fw_status0 = 0xe8, + .card_fw_status1 = 0xe9, + .card_rx_len = 0xea, + .card_rx_unit = 0xeb, + .io_port_0 = 0xe4, + .io_port_1 = 0xe5, + .io_port_2 = 0xe6, + .int_read_to_clear = true, + .host_int_rsr = 0x04, + .card_misc_cfg = 0xD8, + .fw_dump_ctrl = 0xf0, + .fw_dump_start = 0xf1, + .fw_dump_end = 0xf8, +}; + static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = { .helper = "mrvl/sd8688_helper.bin", .firmware = "mrvl/sd8688.bin", @@ -191,6 +214,15 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = { .supports_fw_dump = true, }; +static const struct btmrvl_sdio_device btmrvl_sdio_sd8997 = { + .helper = NULL, + .firmware = "mrvl/sd8997_uapsta.bin", + .reg = &btmrvl_reg_8997, + .support_pscan_win_report = true, + .sd_blksz_fw_dl = 256, + .supports_fw_dump = true, +}; + static const struct sdio_device_id btmrvl_sdio_ids[] = { /* Marvell SD8688 Bluetooth device */ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9105), @@ -210,6 +242,9 @@ static const struct sdio_device_id btmrvl_sdio_ids[] = { /* Marvell SD8897 Bluetooth device */ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912E), .driver_data = (unsigned long)&btmrvl_sdio_sd8897 }, + /* Marvell SD8997 Bluetooth device */ + { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9142), + .driver_data = (unsigned long)&btmrvl_sdio_sd8997 }, { } /* Terminating entry */ }; @@ -1622,3 +1657,4 @@ MODULE_FIRMWARE("mrvl/sd8787_uapsta.bin"); MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin"); MODULE_FIRMWARE("mrvl/sd8887_uapsta.bin"); MODULE_FIRMWARE("mrvl/sd8897_uapsta.bin"); +MODULE_FIRMWARE("mrvl/sd8997_uapsta.bin"); -- cgit v0.10.2 From 8de1c63ba1ccfa8225505e60b405537c2c72673c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 21 Aug 2015 14:13:06 +0200 Subject: wireless: make __freq_reg_info static As pointed out by sparse, this symbol should be static, make it so. Signed-off-by: Johannes Berg diff --git a/net/wireless/reg.c b/net/wireless/reg.c index b144485..70aef72 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1040,8 +1040,8 @@ freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq, return ERR_PTR(-EINVAL); } -const struct ieee80211_reg_rule *__freq_reg_info(struct wiphy *wiphy, - u32 center_freq, u32 min_bw) +static const struct ieee80211_reg_rule * +__freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 min_bw) { const struct ieee80211_regdomain *regd = reg_get_regdomain(wiphy); const struct ieee80211_reg_rule *reg_rule = NULL; -- cgit v0.10.2 From fc58c47ef1ace65c5c1c94f2e96578e7b04aad64 Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Sat, 15 Aug 2015 22:04:01 +0300 Subject: mac80211: process skb_queue while scanning in HW Queued frames aren't processed during scan, which results in an inability to complete the BA session establishment until the scan ends. Since we can't tx frames until the BA agreement setup is complete, it might result in a very large latency during scan. Fix this by allowing to process queued skbs while scanning in HW. This should be ok since the devices which support hw scan should be able to handle tx/rx while scanning. During SW scan, mac80211 drops any txed frames besides probes and NDPs, so it is still needed to delay processing of the queued frames till the SW scan is done. Signed-off-by: Andrei Otcheretianski Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 6964fc6..42d7f0f 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1204,7 +1204,7 @@ static void ieee80211_iface_work(struct work_struct *work) if (!ieee80211_sdata_running(sdata)) return; - if (local->scanning) + if (test_bit(SCAN_SW_SCANNING, &local->scanning)) return; if (!ieee80211_can_run_worker(local)) -- cgit v0.10.2 From 82c0cc90d6268a3cd3ee058257d2146188326452 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sat, 15 Aug 2015 22:39:46 +0300 Subject: mac80211: debugfs: add file to disallow TDLS wider-bw Sometimes we are interested in testing TDLS performance in a specific width setting. Add the ability to disable the wider-band feature, thereby allowing the TDLS channel width to be controlled by the BSS width. Signed-off-by: Arik Nemtsov Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 685ec13..1ca972e 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1156,6 +1156,7 @@ static int sta_apply_parameters(struct ieee80211_local *local, set_sta_flag(sta, WLAN_STA_TDLS_CHAN_SWITCH); if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && + !sdata->u.mgd.tdls_wider_bw_prohibited && ieee80211_hw_check(&local->hw, TDLS_WIDER_BW) && params->ext_capab_len >= 8 && params->ext_capab[7] & WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 1021e87..f1580e9 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -455,6 +455,34 @@ static ssize_t ieee80211_if_parse_uapsd_max_sp_len( } IEEE80211_IF_FILE_RW(uapsd_max_sp_len); +static ssize_t ieee80211_if_fmt_tdls_wider_bw( + const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) +{ + const struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + bool tdls_wider_bw; + + tdls_wider_bw = ieee80211_hw_check(&sdata->local->hw, TDLS_WIDER_BW) && + !ifmgd->tdls_wider_bw_prohibited; + + return snprintf(buf, buflen, "%d\n", tdls_wider_bw); +} + +static ssize_t ieee80211_if_parse_tdls_wider_bw( + struct ieee80211_sub_if_data *sdata, const char *buf, int buflen) +{ + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + u8 val; + int ret; + + ret = kstrtou8(buf, 0, &val); + if (ret) + return ret; + + ifmgd->tdls_wider_bw_prohibited = !val; + return buflen; +} +IEEE80211_IF_FILE_RW(tdls_wider_bw); + /* AP attributes */ 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); @@ -614,6 +642,7 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD_MODE(beacon_loss, 0200); DEBUGFS_ADD_MODE(uapsd_queues, 0600); DEBUGFS_ADD_MODE(uapsd_max_sp_len, 0600); + DEBUGFS_ADD_MODE(tdls_wider_bw, 0600); } static void add_ap_files(struct ieee80211_sub_if_data *sdata) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 6e52659..65f4faa 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -535,6 +535,7 @@ struct ieee80211_if_managed { struct sk_buff *teardown_skb; /* A copy to send through the AP */ spinlock_t teardown_lock; /* To lock changing teardown_skb */ bool tdls_chan_switch_prohibited; + bool tdls_wider_bw_prohibited; /* WMM-AC TSPEC support */ struct ieee80211_sta_tx_tspec tx_tspec[IEEE80211_NUM_ACS]; diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index aee701a..1bacea7 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -41,9 +41,11 @@ static void ieee80211_tdls_add_ext_capab(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_local *local = sdata->local; + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; bool chan_switch = local->hw.wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH; - bool wider_band = ieee80211_hw_check(&local->hw, TDLS_WIDER_BW); + bool wider_band = ieee80211_hw_check(&local->hw, TDLS_WIDER_BW) && + !ifmgd->tdls_wider_bw_prohibited; enum ieee80211_band band = ieee80211_get_sdata_band(sdata); struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band]; bool vht = sband && sband->vht_cap.vht_supported; -- cgit v0.10.2 From dd55ab59b6234c73522dc533757e89e6a77c2c38 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sat, 15 Aug 2015 22:39:48 +0300 Subject: mac80211: TDLS: check reg with IR-relax on chandef upgrade When checking if a TDLS chandef can be upgraded, IR-relaxation can be taken into account to allow more channels. Signed-off-by: Arik Nemtsov Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 1bacea7..52f3187 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -333,8 +333,8 @@ ieee80211_tdls_chandef_vht_upgrade(struct ieee80211_sub_if_data *sdata, /* proceed to downgrade the chandef until usable or the same */ while (uc.width > max_width && - !cfg80211_reg_can_beacon(sdata->local->hw.wiphy, - &uc, sdata->wdev.iftype)) + !cfg80211_reg_can_beacon_relax(sdata->local->hw.wiphy, &uc, + sdata->wdev.iftype)) ieee80211_chandef_downgrade(&uc); if (!cfg80211_chandef_identical(&uc, &sta->tdls_chandef)) { -- cgit v0.10.2 From 7bdbe400d1b2aac116513f90b75969ad2365fba6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 15 Aug 2015 22:39:49 +0300 Subject: nl80211: support vendor dumpit commands In order to transfer many items in vendor commands, support the dumpit netlink method for them. Signed-off-by: Johannes Berg diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f0889a2..0f54c9e 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2971,12 +2971,21 @@ enum wiphy_vendor_command_flags { * @doit: callback for the operation, note that wdev is %NULL if the * flags didn't ask for a wdev and non-%NULL otherwise; the data * pointer may be %NULL if userspace provided no data at all + * @dumpit: dump callback, for transferring bigger/multiple items. The + * @storage points to cb->args[5], ie. is preserved over the multiple + * dumpit calls. + * It's recommended to not have the same sub command with both @doit and + * @dumpit, so that userspace can assume certain ones are get and others + * are used with dump requests. */ struct wiphy_vendor_command { struct nl80211_vendor_cmd_info info; u32 flags; int (*doit)(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len); + int (*dumpit)(struct wiphy *wiphy, struct wireless_dev *wdev, + struct sk_buff *skb, const void *data, int data_len, + unsigned long *storage); }; /** diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5d8748b..a4e6c95 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3,6 +3,7 @@ * * Copyright 2006-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH + * Copyright 2015 Intel Deutschland GmbH */ #include @@ -9938,6 +9939,9 @@ static int nl80211_vendor_cmd(struct sk_buff *skb, struct genl_info *info) if (!wdev->netdev && !wdev->p2p_started) return -ENETDOWN; } + + if (!vcmd->doit) + return -EOPNOTSUPP; } else { wdev = NULL; } @@ -9957,6 +9961,193 @@ static int nl80211_vendor_cmd(struct sk_buff *skb, struct genl_info *info) return -EOPNOTSUPP; } +static int nl80211_prepare_vendor_dump(struct sk_buff *skb, + struct netlink_callback *cb, + struct cfg80211_registered_device **rdev, + struct wireless_dev **wdev) +{ + u32 vid, subcmd; + unsigned int i; + int vcmd_idx = -1; + int err; + void *data = NULL; + unsigned int data_len = 0; + + rtnl_lock(); + + if (cb->args[0]) { + /* subtract the 1 again here */ + struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1); + struct wireless_dev *tmp; + + if (!wiphy) { + err = -ENODEV; + goto out_unlock; + } + *rdev = wiphy_to_rdev(wiphy); + *wdev = NULL; + + if (cb->args[1]) { + list_for_each_entry(tmp, &(*rdev)->wdev_list, list) { + if (tmp->identifier == cb->args[1] - 1) { + *wdev = tmp; + break; + } + } + } + + /* keep rtnl locked in successful case */ + return 0; + } + + err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, + nl80211_fam.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]) { + err = -EINVAL; + goto out_unlock; + } + + *wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk), + nl80211_fam.attrbuf); + if (IS_ERR(*wdev)) + *wdev = NULL; + + *rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk), + nl80211_fam.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]); + + for (i = 0; i < (*rdev)->wiphy.n_vendor_commands; i++) { + const struct wiphy_vendor_command *vcmd; + + vcmd = &(*rdev)->wiphy.vendor_commands[i]; + + if (vcmd->info.vendor_id != vid || vcmd->info.subcmd != subcmd) + continue; + + if (!vcmd->dumpit) { + err = -EOPNOTSUPP; + goto out_unlock; + } + + vcmd_idx = i; + break; + } + + if (vcmd_idx < 0) { + err = -EOPNOTSUPP; + 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]); + } + + /* 0 is the first index - add 1 to parse only once */ + cb->args[0] = (*rdev)->wiphy_idx + 1; + /* add 1 to know if it was NULL */ + cb->args[1] = *wdev ? (*wdev)->identifier + 1 : 0; + cb->args[2] = vcmd_idx; + cb->args[3] = (unsigned long)data; + cb->args[4] = data_len; + + /* keep rtnl locked in successful case */ + return 0; + out_unlock: + rtnl_unlock(); + return err; +} + +static int nl80211_vendor_cmd_dump(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct cfg80211_registered_device *rdev; + struct wireless_dev *wdev; + unsigned int vcmd_idx; + const struct wiphy_vendor_command *vcmd; + void *data; + int data_len; + int err; + struct nlattr *vendor_data; + + err = nl80211_prepare_vendor_dump(skb, cb, &rdev, &wdev); + if (err) + return err; + + vcmd_idx = cb->args[2]; + data = (void *)cb->args[3]; + data_len = cb->args[4]; + vcmd = &rdev->wiphy.vendor_commands[vcmd_idx]; + + if (vcmd->flags & (WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV)) { + if (!wdev) + return -EINVAL; + if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_NETDEV && + !wdev->netdev) + 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) + return -ENETDOWN; + } + } + + while (1) { + void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + NL80211_CMD_VENDOR); + if (!hdr) + break; + + if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || + (wdev && nla_put_u64(skb, NL80211_ATTR_WDEV, + wdev_id(wdev)))) { + genlmsg_cancel(skb, hdr); + break; + } + + vendor_data = nla_nest_start(skb, NL80211_ATTR_VENDOR_DATA); + if (!vendor_data) { + genlmsg_cancel(skb, hdr); + break; + } + + err = vcmd->dumpit(&rdev->wiphy, wdev, skb, data, data_len, + (unsigned long *)&cb->args[5]); + nla_nest_end(skb, vendor_data); + + if (err == -ENOBUFS || err == -ENOENT) { + genlmsg_cancel(skb, hdr); + break; + } else if (err) { + genlmsg_cancel(skb, hdr); + goto out; + } + + genlmsg_end(skb, hdr); + } + + err = skb->len; + out: + rtnl_unlock(); + return err; +} + struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy, enum nl80211_commands cmd, enum nl80211_attrs attr, @@ -10994,6 +11185,7 @@ static const struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_VENDOR, .doit = nl80211_vendor_cmd, + .dumpit = nl80211_vendor_cmd_dump, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_WIPHY | -- cgit v0.10.2 From 1b09b5568e5f46c6dfb781d7c1dfad431a6d8ec1 Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Sat, 15 Aug 2015 22:39:50 +0300 Subject: mac80211: introduce per vif frame registration API Currently the cfg80211's frame registration api receives wdev, however mac80211 assumes per device filter configuration and ignores wdev. Per device filtering is too wasteful, especially for multi-channel devices. Introduce new per vif frame registration API and use it for probe request registrations in ieee80211_mgmt_frame_register() Also call directly to ieee80211_configure_filter instead of using a work since it is now allowed to sleep in ieee80211_mgmt_frame_register. Signed-off-by: Andrei Otcheretianski Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg diff --git a/include/net/mac80211.h b/include/net/mac80211.h index e3314e5..16786450 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -5,6 +5,7 @@ * Copyright 2006-2007 Jiri Benc * Copyright 2007-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH + * Copyright (C) 2015 Intel Deutschland GmbH * * 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 @@ -1358,6 +1359,8 @@ enum ieee80211_vif_flags { * @debugfs_dir: debugfs dentry, can be used by drivers to create own per * interface debug files. Note that it will be NULL for the virtual * monitor interface (if that is requested.) + * @probe_req_reg: probe requests should be reported to mac80211 for this + * interface. * @drv_priv: data area for driver use, will always be aligned to * sizeof(void *). * @txq: the multicast data TX queue (if driver uses the TXQ abstraction) @@ -1382,6 +1385,8 @@ struct ieee80211_vif { struct dentry *debugfs_dir; #endif + unsigned int probe_req_reg; + /* must be last */ u8 drv_priv[0] __aligned(sizeof(void *)); }; @@ -2825,6 +2830,13 @@ enum ieee80211_reconfig_type { * See the section "Frame filtering" for more information. * This callback must be implemented and can sleep. * + * @config_iface_filter: Configure the interface's RX filter. + * This callback is optional and is used to configure which frames + * should be passed to mac80211. The filter_flags is the combination + * of FIF_* flags. The changed_flags is a bit mask that indicates + * which flags are changed. + * This callback can sleep. + * * @set_tim: Set TIM bit. mac80211 calls this function when a TIM bit * must be set or cleared for a given STA. Must be atomic. * @@ -3264,6 +3276,10 @@ struct ieee80211_ops { unsigned int changed_flags, unsigned int *total_flags, u64 multicast); + void (*config_iface_filter)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + unsigned int filter_flags, + unsigned int changed_flags); int (*set_tim)(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set); int (*set_key)(struct ieee80211_hw *hw, enum set_key_cmd cmd, diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 1ca972e..9eab783 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -3516,18 +3516,32 @@ static void ieee80211_mgmt_frame_register(struct wiphy *wiphy, u16 frame_type, bool reg) { struct ieee80211_local *local = wiphy_priv(wiphy); + struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); switch (frame_type) { case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ: - if (reg) + if (reg) { local->probe_req_reg++; - else - local->probe_req_reg--; + sdata->vif.probe_req_reg++; + } else { + if (local->probe_req_reg) + local->probe_req_reg--; + + if (sdata->vif.probe_req_reg) + sdata->vif.probe_req_reg--; + } if (!local->open_count) break; - ieee80211_queue_work(&local->hw, &local->reconfig_filter); + if (sdata->vif.probe_req_reg == 1) + drv_config_iface_filter(local, sdata, FIF_PROBE_REQ, + FIF_PROBE_REQ); + else if (sdata->vif.probe_req_reg == 0) + drv_config_iface_filter(local, sdata, 0, + FIF_PROBE_REQ); + + ieee80211_configure_filter(local); break; default: break; diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 02d9133..157b20b 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -260,6 +260,22 @@ static inline void drv_configure_filter(struct ieee80211_local *local, trace_drv_return_void(local); } +static inline void drv_config_iface_filter(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + unsigned int filter_flags, + unsigned int changed_flags) +{ + might_sleep(); + + trace_drv_config_iface_filter(local, sdata, filter_flags, + changed_flags); + if (local->ops->config_iface_filter) + local->ops->config_iface_filter(&local->hw, &sdata->vif, + filter_flags, + changed_flags); + trace_drv_return_void(local); +} + static inline int drv_set_tim(struct ieee80211_local *local, struct ieee80211_sta *sta, bool set) { diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 6f14591..b5960b9 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -497,6 +497,36 @@ TRACE_EVENT(drv_configure_filter, ) ); +TRACE_EVENT(drv_config_iface_filter, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + unsigned int filter_flags, + unsigned int changed_flags), + + TP_ARGS(local, sdata, filter_flags, changed_flags), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + __field(unsigned int, filter_flags) + __field(unsigned int, changed_flags) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + __entry->filter_flags = filter_flags; + __entry->changed_flags = changed_flags; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT + " filter_flags: %#x changed_flags: %#x", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->filter_flags, + __entry->changed_flags + ) +); + TRACE_EVENT(drv_set_tim, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sta *sta, bool set), -- cgit v0.10.2 From e3abc8ff0fc18b3925fd5d5c5fbd1613856f4e7c Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 16 Aug 2015 11:13:22 +0300 Subject: mac80211: allow to transmit A-MSDU within A-MPDU Advertise the capability to send A-MSDU within A-MPDU in the AddBA request sent by mac80211. Let the driver know about the peer's capabilities. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 64674c9..b04e769 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -6144,7 +6144,7 @@ static int ath10k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) + u8 buf_size, bool amsdu) { struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 172a9ff4a..a680a97 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1659,7 +1659,7 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, - u16 tid, u16 *ssn, u8 buf_size) + u16 tid, u16 *ssn, u8 buf_size, bool amsdu) { struct ath9k_htc_priv *priv = hw->priv; struct ath9k_htc_sta *ista; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index c27143b..323eb33 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1856,7 +1856,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, - u16 tid, u16 *ssn, u8 buf_size) + u16 tid, u16 *ssn, u8 buf_size, bool amsdu) { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 170c209..19d3d64 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1415,7 +1415,7 @@ static int carl9170_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, - u16 tid, u16 *ssn, u8 buf_size) + u16 tid, u16 *ssn, u8 buf_size, bool amsdu) { struct ar9170 *ar = hw->priv; struct carl9170_sta_info *sta_info = (void *) sta->drv_priv; diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index 900e72a..7c169ab 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -859,7 +859,7 @@ static int wcn36xx_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) + u8 buf_size, bool amsdu) { struct wcn36xx *wcn = hw->priv; struct wcn36xx_sta *sta_priv = NULL; diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index d2c5747..bec2dc1 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -820,7 +820,7 @@ brcms_ops_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) + u8 buf_size, bool amsdu) { struct brcms_info *wl = hw->priv; struct scb *scb = &wl->wlc->pri_scb; diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c index b86500b..95a7fdb 100644 --- a/drivers/net/wireless/cw1200/sta.c +++ b/drivers/net/wireless/cw1200/sta.c @@ -2137,7 +2137,7 @@ int cw1200_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) + u8 buf_size, bool amsdu) { /* Aggregation is implemented fully in firmware, * including block ack negotiation. Do not allow diff --git a/drivers/net/wireless/cw1200/sta.h b/drivers/net/wireless/cw1200/sta.h index b7e386b..bebb337 100644 --- a/drivers/net/wireless/cw1200/sta.h +++ b/drivers/net/wireless/cw1200/sta.h @@ -111,7 +111,7 @@ int cw1200_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size); + u8 buf_size, bool amsdu); void cw1200_suspend_resume(struct cw1200_common *priv, struct wsm_suspend_resume *arg); diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index 44fa422..6656215 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -5984,7 +5984,7 @@ int il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 * ssn, - u8 buf_size) + u8 buf_size, bool amsdu) { struct il_priv *il = hw->priv; int ret = -EINVAL; diff --git a/drivers/net/wireless/iwlegacy/4965.h b/drivers/net/wireless/iwlegacy/4965.h index 3a57f71..8ab8706 100644 --- a/drivers/net/wireless/iwlegacy/4965.h +++ b/drivers/net/wireless/iwlegacy/4965.h @@ -184,7 +184,7 @@ void il4965_mac_update_tkip_key(struct ieee80211_hw *hw, int il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 * ssn, - u8 buf_size); + u8 buf_size, bool amsdu); int il4965_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); void diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 453f7c3..b3ad34e 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -731,7 +731,7 @@ static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) + u8 buf_size, bool amsdu) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); int ret = -EINVAL; diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index aa8c2b7..f70452c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -820,7 +820,7 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, - u16 *ssn, u8 buf_size) + u16 *ssn, u8 buf_size, bool amsdu) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); int ret; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 520bef8..2af2f3d 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1819,7 +1819,7 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) + u8 buf_size, bool amsdu) { switch (action) { case IEEE80211_AMPDU_TX_START: diff --git a/drivers/net/wireless/mediatek/mt7601u/main.c b/drivers/net/wireless/mediatek/mt7601u/main.c index 169384b..f715eee 100644 --- a/drivers/net/wireless/mediatek/mt7601u/main.c +++ b/drivers/net/wireless/mediatek/mt7601u/main.c @@ -335,7 +335,8 @@ static int mt7601u_set_rts_threshold(struct ieee80211_hw *hw, u32 value) static int mt76_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size) + struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size, + bool amsdu) { struct mt7601u_dev *dev = hw->priv; struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv; diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 9420fc6..30e3aaa 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -5423,7 +5423,7 @@ static int mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) + u8 buf_size, bool amsdu) { int i, rc = 0; diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 7e804324..b5bcc93 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -664,6 +664,7 @@ static int rsi_mac80211_set_key(struct ieee80211_hw *hw, * @tid: Traffic identifier. * @ssn: Pointer to ssn value. * @buf_size: Buffer size (for kernel version > 2.6.38). + * @amsdu: is AMSDU in AMPDU allowed * * Return: status: 0 on success, negative error code on failure. */ @@ -673,7 +674,8 @@ static int rsi_mac80211_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_sta *sta, unsigned short tid, unsigned short *ssn, - unsigned char buf_size) + unsigned char buf_size, + bool amsdu) { int status = -EOPNOTSUPP; struct rsi_hw *adapter = hw->priv; diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 9524564..9733b31 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -7937,7 +7937,7 @@ EXPORT_SYMBOL_GPL(rt2800_get_tsf); int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) + u8 buf_size, bool amsdu) { struct rt2x00_sta *sta_priv = (struct rt2x00_sta *)sta->drv_priv; int ret = 0; diff --git a/drivers/net/wireless/rt2x00/rt2800lib.h b/drivers/net/wireless/rt2x00/rt2800lib.h index 1609b8a..440790b 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.h +++ b/drivers/net/wireless/rt2x00/rt2800lib.h @@ -220,7 +220,7 @@ u64 rt2800_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif); int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size); + u8 buf_size, bool amsdu); int rt2800_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey); void rt2800_disable_wpdma(struct rt2x00_dev *rt2x00dev); diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c index 585d088..c925a4d 100644 --- a/drivers/net/wireless/rtlwifi/core.c +++ b/drivers/net/wireless/rtlwifi/core.c @@ -1373,7 +1373,7 @@ static int rtl_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) + u8 buf_size, bool amsdu) { struct rtl_priv *rtlpriv = rtl_priv(hw); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index e819369..ec7f6af 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5263,7 +5263,7 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) + u8 buf_size, bool amsdu) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index cfa906f..19eb9ec 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1379,6 +1379,7 @@ struct ieee80211_ht_operation { /* block-ack parameters */ +#define IEEE80211_ADDBA_PARAM_AMSDU_MASK 0x0001 #define IEEE80211_ADDBA_PARAM_POLICY_MASK 0x0002 #define IEEE80211_ADDBA_PARAM_TID_MASK 0x003C #define IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK 0xFFC0 diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 16786450..f28cbc0 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3026,6 +3026,9 @@ enum ieee80211_reconfig_type { * buffer size of 8. Correct ways to retransmit #1 would be: * - TX: 1 or 18 or 81 * Even "189" would be wrong since 1 could be lost again. + * The @amsdu parameter is valid when the action is set to + * %IEEE80211_AMPDU_TX_OPERATIONAL and indicates the peer's ability + * to receive A-MSDU within A-MPDU. * * Returns a negative error code on failure. * The callback can sleep. @@ -3363,7 +3366,7 @@ struct ieee80211_ops { struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size); + u8 buf_size, bool amsdu); int (*get_survey)(struct ieee80211_hw *hw, int idx, struct survey_info *survey); void (*rfkill_poll)(struct ieee80211_hw *hw); diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 5c564a6..6ebe861 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -79,7 +79,7 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, (int)reason); if (drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_STOP, - &sta->sta, tid, NULL, 0)) + &sta->sta, tid, NULL, 0, false)) sdata_info(sta->sdata, "HW problem - can not stop rx aggregation for %pM tid %d\n", sta->sta.addr, tid); @@ -321,7 +321,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, __skb_queue_head_init(&tid_agg_rx->reorder_buf[i]); ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START, - &sta->sta, tid, &start_seq_num, 0); + &sta->sta, tid, &start_seq_num, 0, false); ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n", sta->sta.addr, tid, ret); if (ret) { diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index c8ba2e7..a758eb84 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -97,7 +97,8 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata, mgmt->u.action.u.addba_req.action_code = WLAN_ACTION_ADDBA_REQ; mgmt->u.action.u.addba_req.dialog_token = dialog_token; - capab = (u16)(1 << 1); /* bit 1 aggregation policy */ + capab = (u16)(1 << 0); /* bit 0 A-MSDU support */ + capab |= (u16)(1 << 1); /* bit 1 aggregation policy */ capab |= (u16)(tid << 2); /* bit 5:2 TID number */ capab |= (u16)(agg_size << 6); /* bit 15:6 max size of aggergation */ @@ -331,7 +332,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, return -EALREADY; ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_TX_STOP_FLUSH_CONT, - &sta->sta, tid, NULL, 0); + &sta->sta, tid, NULL, 0, false); WARN_ON_ONCE(ret); return 0; } @@ -381,7 +382,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, tid_tx->tx_stop = reason == AGG_STOP_LOCAL_REQUEST; ret = drv_ampdu_action(local, sta->sdata, action, - &sta->sta, tid, NULL, 0); + &sta->sta, tid, NULL, 0, false); /* HW shall not deny going back to legacy */ if (WARN_ON(ret)) { @@ -469,7 +470,7 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) start_seq_num = sta->tid_seq[tid] >> 4; ret = drv_ampdu_action(local, sdata, IEEE80211_AMPDU_TX_START, - &sta->sta, tid, &start_seq_num, 0); + &sta->sta, tid, &start_seq_num, 0, false); if (ret) { ht_dbg(sdata, "BA request denied - HW unavailable for %pM tid %d\n", @@ -693,7 +694,8 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local, drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_TX_OPERATIONAL, - &sta->sta, tid, NULL, tid_tx->buf_size); + &sta->sta, tid, NULL, tid_tx->buf_size, + tid_tx->amsdu); /* * synchronize with TX path, while splicing the TX path @@ -918,8 +920,10 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, struct tid_ampdu_tx *tid_tx; u16 capab, tid; u8 buf_size; + bool amsdu; capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab); + amsdu = capab & IEEE80211_ADDBA_PARAM_AMSDU_MASK; tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6; @@ -968,6 +972,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, } tid_tx->buf_size = buf_size; + tid_tx->amsdu = amsdu; if (test_bit(HT_AGG_STATE_DRV_READY, &tid_tx->state)) ieee80211_agg_tx_operational(local, sta, tid); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 157b20b..31482e2 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -734,7 +734,7 @@ static inline int drv_ampdu_action(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, - u16 *ssn, u8 buf_size) + u16 *ssn, u8 buf_size, bool amsdu) { int ret = -EOPNOTSUPP; @@ -744,11 +744,12 @@ static inline int drv_ampdu_action(struct ieee80211_local *local, if (!check_sdata_in_driver(sdata)) return -EIO; - trace_drv_ampdu_action(local, sdata, action, sta, tid, ssn, buf_size); + trace_drv_ampdu_action(local, sdata, action, sta, tid, + ssn, buf_size, amsdu); if (local->ops->ampdu_action) ret = local->ops->ampdu_action(&local->hw, &sdata->vif, action, - sta, tid, ssn, buf_size); + sta, tid, ssn, buf_size, amsdu); trace_drv_return_int(local, ret); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index b087c71..d5ded87 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -133,6 +133,7 @@ enum ieee80211_agg_stop_reason { * @buf_size: reorder buffer size at receiver * @failed_bar_ssn: ssn of the last failed BAR tx attempt * @bar_pending: BAR needs to be re-sent + * @amsdu: support A-MSDU withing A-MDPU * * This structure's lifetime is managed by RCU, assignments to * the array holding it must hold the aggregation mutex. @@ -158,6 +159,7 @@ struct tid_ampdu_tx { u16 failed_bar_ssn; bool bar_pending; + bool amsdu; }; /** diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index b5960b9..314e3bd 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -974,9 +974,9 @@ TRACE_EVENT(drv_ampdu_action, struct ieee80211_sub_if_data *sdata, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, - u16 *ssn, u8 buf_size), + u16 *ssn, u8 buf_size, bool amsdu), - TP_ARGS(local, sdata, action, sta, tid, ssn, buf_size), + TP_ARGS(local, sdata, action, sta, tid, ssn, buf_size, amsdu), TP_STRUCT__entry( LOCAL_ENTRY @@ -985,6 +985,7 @@ TRACE_EVENT(drv_ampdu_action, __field(u16, tid) __field(u16, ssn) __field(u8, buf_size) + __field(bool, amsdu) VIF_ENTRY ), @@ -996,12 +997,13 @@ TRACE_EVENT(drv_ampdu_action, __entry->tid = tid; __entry->ssn = ssn ? *ssn : 0; __entry->buf_size = buf_size; + __entry->amsdu = amsdu; ), TP_printk( - LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " action:%d tid:%d buf:%d", + LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " action:%d tid:%d buf:%d amsdu:%d", LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->action, - __entry->tid, __entry->buf_size + __entry->tid, __entry->buf_size, __entry->amsdu ) ); -- cgit v0.10.2 From 46cad4b7a131a215159d889fa88d0dc71d581908 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 15 Aug 2015 22:39:54 +0300 Subject: mac80211: remove direct probe step before authentication The direct probe step before authentication was done mostly for two reasons: 1) the BSS data could be stale 2) the beacon might not have included all IEs The concern (1) doesn't really seem to be relevant any more as we time out BSS information after about 30 seconds, and in fact the original patch only did the direct probe if the data was older than the BSS timeout to begin with. This condition got (likely inadvertedly) removed later though. Analysing this in more detail shows that since we mostly use data from the association response, the only real reason for needing the probe response was that the code validates the WMM parameters, and those are optional in beacons. As the previous patches removed that behaviour, we can now remove the direct probe step entirely. Signed-off-by: Johannes Berg diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 705ef1d..6daadf2 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3262,16 +3262,6 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, if (ifmgd->associated && ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid)) ieee80211_reset_ap_probe(sdata); - - if (ifmgd->auth_data && !ifmgd->auth_data->bss->proberesp_ies && - ether_addr_equal(mgmt->bssid, ifmgd->auth_data->bss->bssid)) { - /* got probe response, continue with auth */ - sdata_info(sdata, "direct probe responded\n"); - ifmgd->auth_data->tries = 0; - ifmgd->auth_data->timeout = jiffies; - ifmgd->auth_data->timeout_started = true; - run_again(sdata, ifmgd->auth_data->timeout); - } } /* @@ -3717,12 +3707,14 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata, reason); } -static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) +static int ieee80211_auth(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_mgd_auth_data *auth_data = ifmgd->auth_data; u32 tx_flags = 0; + u16 trans = 1; + u16 status = 0; sdata_assert_lock(sdata); @@ -3746,54 +3738,27 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) drv_mgd_prepare_tx(local, sdata); - if (auth_data->bss->proberesp_ies) { - u16 trans = 1; - u16 status = 0; - - sdata_info(sdata, "send auth to %pM (try %d/%d)\n", - auth_data->bss->bssid, auth_data->tries, - IEEE80211_AUTH_MAX_TRIES); - - auth_data->expected_transaction = 2; + sdata_info(sdata, "send auth to %pM (try %d/%d)\n", + auth_data->bss->bssid, auth_data->tries, + IEEE80211_AUTH_MAX_TRIES); - if (auth_data->algorithm == WLAN_AUTH_SAE) { - trans = auth_data->sae_trans; - status = auth_data->sae_status; - auth_data->expected_transaction = trans; - } + auth_data->expected_transaction = 2; - if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) - tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS | - IEEE80211_TX_INTFL_MLME_CONN_TX; - - ieee80211_send_auth(sdata, trans, auth_data->algorithm, status, - auth_data->data, auth_data->data_len, - auth_data->bss->bssid, - auth_data->bss->bssid, NULL, 0, 0, - tx_flags); - } else { - const u8 *ssidie; + if (auth_data->algorithm == WLAN_AUTH_SAE) { + trans = auth_data->sae_trans; + status = auth_data->sae_status; + auth_data->expected_transaction = trans; + } - sdata_info(sdata, "direct probe to %pM (try %d/%i)\n", - auth_data->bss->bssid, auth_data->tries, - IEEE80211_AUTH_MAX_TRIES); + if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) + tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS | + IEEE80211_TX_INTFL_MLME_CONN_TX; - rcu_read_lock(); - ssidie = ieee80211_bss_get_ie(auth_data->bss, WLAN_EID_SSID); - if (!ssidie) { - rcu_read_unlock(); - return -EINVAL; - } - /* - * Direct probe is sent to broadcast address as some APs - * will not answer to direct packet in unassociated state. - */ - ieee80211_send_probe_req(sdata, sdata->vif.addr, NULL, - ssidie + 2, ssidie[1], - NULL, 0, (u32) -1, true, 0, - auth_data->bss->channel, false); - rcu_read_unlock(); - } + ieee80211_send_auth(sdata, trans, auth_data->algorithm, status, + auth_data->data, auth_data->data_len, + auth_data->bss->bssid, + auth_data->bss->bssid, NULL, 0, 0, + tx_flags); if (tx_flags == 0) { auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; @@ -3874,8 +3839,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) bool status_acked = ifmgd->status_acked; ifmgd->status_received = false; - if (ifmgd->auth_data && - (ieee80211_is_probe_req(fc) || ieee80211_is_auth(fc))) { + if (ifmgd->auth_data && ieee80211_is_auth(fc)) { if (status_acked) { ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT_SHORT; @@ -3906,7 +3870,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) * so let's just kill the auth data */ ieee80211_destroy_auth_data(sdata, false); - } else if (ieee80211_probe_auth(sdata)) { + } else if (ieee80211_auth(sdata)) { u8 bssid[ETH_ALEN]; struct ieee80211_event event = { .type = MLME_EVENT, @@ -4597,7 +4561,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, if (err) goto err_clear; - err = ieee80211_probe_auth(sdata); + err = ieee80211_auth(sdata); if (err) { sta_info_destroy_addr(sdata, req->bss->bssid); goto err_clear; -- cgit v0.10.2 From 99e7ca44bb910f0cbfda5d9008e8517df0ebc939 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sat, 15 Aug 2015 22:39:51 +0300 Subject: mac80211: allow the driver to advertise A-MSDU within A-MPDU Rx support Drivers may be interested in receiving A-MSDU within A-MDPU. Not all the devices may be able to do so, make it configurable. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg diff --git a/include/net/mac80211.h b/include/net/mac80211.h index f28cbc0..d62d6f4 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1897,6 +1897,9 @@ struct ieee80211_txq { * @IEEE80211_HW_TDLS_WIDER_BW: The device/driver supports wider bandwidth * than then BSS bandwidth for a TDLS link on the base channel. * + * @IEEE80211_HW_SUPPORTS_AMSDU_IN_AMPDU: The driver supports receiving A-MSDUs + * within A-MPDU. + * * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays */ enum ieee80211_hw_flags { @@ -1930,6 +1933,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_SUPPORTS_CLONED_SKBS, IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS, IEEE80211_HW_TDLS_WIDER_BW, + IEEE80211_HW_SUPPORTS_AMSDU_IN_AMPDU, /* keep last, obviously */ NUM_IEEE80211_HW_FLAGS diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 6ebe861..10ad4ac 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -189,6 +189,7 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; + bool amsdu = ieee80211_hw_check(&local->hw, SUPPORTS_AMSDU_IN_AMPDU); u16 capab; skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom); @@ -217,7 +218,8 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d mgmt->u.action.u.addba_resp.action_code = WLAN_ACTION_ADDBA_RESP; mgmt->u.action.u.addba_resp.dialog_token = dialog_token; - capab = (u16)(policy << 1); /* bit 1 aggregation policy */ + capab = (u16)(amsdu << 0); /* bit 0 A-MSDU support */ + capab |= (u16)(policy << 1); /* bit 1 aggregation policy */ capab |= (u16)(tid << 2); /* bit 5:2 TID number */ capab |= (u16)(buf_size << 6); /* bit 15:6 max size of aggregation */ diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index ced6bf3..41726fd 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -123,6 +123,7 @@ static const char *hw_flag_names[NUM_IEEE80211_HW_FLAGS + 1] = { FLAG(SUPPORTS_CLONED_SKBS), FLAG(SINGLE_SCAN_ON_ALL_BANDS), FLAG(TDLS_WIDER_BW), + FLAG(SUPPORTS_AMSDU_IN_AMPDU), /* keep last for the build bug below */ (void *)0x1 -- cgit v0.10.2 From f020ae40b0c969d3fd3b320d0a05e62d5553ff72 Mon Sep 17 00:00:00 2001 From: Chun-Yeow Yeoh Date: Fri, 4 Sep 2015 10:58:05 +0800 Subject: mac80211: zero center freq segment 2 in VHT oper IE Clear the Channel Center Frequency Segment 2 in VHT operation IEs to avoid sending non-zero values if the SKB wasn't zeroed before adding the VHT operation IE. Signed-off-by: Chun-Yeow Yeoh [change commit message a bit - not necessarily just mesh related] Signed-off-by: Johannes Berg diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 1104421..f167056 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2324,6 +2324,8 @@ u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, if (chandef->center_freq2) vht_oper->center_freq_seg2_idx = ieee80211_frequency_to_channel(chandef->center_freq2); + else + vht_oper->center_freq_seg2_idx = 0x00; switch (chandef->width) { case NL80211_CHAN_WIDTH_160: -- cgit v0.10.2 From c85fb53c4fa6521352028c40ce096a808aabd389 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Thu, 27 Aug 2015 09:00:18 -0400 Subject: mac80211: implement VHT support for mesh Implement the basics required for supporting very high throughput with mesh: include VHT information elements in beacons, probe responses, and peering action frames, and check for compatible VHT configurations when peering. Signed-off-by: Bob Copeland Signed-off-by: Johannes Berg diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index e06a5ca..62b3e29 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -94,6 +94,9 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan, ie->ht_operation, &sta_chan_def); + ieee80211_vht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan, + ie->vht_operation, &sta_chan_def); + if (!cfg80211_chandef_compatible(&sdata->vif.bss_conf.chandef, &sta_chan_def)) return false; @@ -436,8 +439,6 @@ int mesh_add_ht_oper_ie(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_channel *channel; - enum nl80211_channel_type channel_type = - cfg80211_get_chandef_type(&sdata->vif.bss_conf.chandef); struct ieee80211_supported_band *sband; struct ieee80211_sta_ht_cap *ht_cap; u8 *pos; @@ -454,7 +455,10 @@ int mesh_add_ht_oper_ie(struct ieee80211_sub_if_data *sdata, sband = local->hw.wiphy->bands[channel->band]; ht_cap = &sband->ht_cap; - if (!ht_cap->ht_supported || channel_type == NL80211_CHAN_NO_HT) + if (!ht_cap->ht_supported || + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT || + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 || + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10) return 0; if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_operation)) @@ -467,6 +471,68 @@ int mesh_add_ht_oper_ie(struct ieee80211_sub_if_data *sdata, return 0; } +int mesh_add_vht_cap_ie(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) +{ + struct ieee80211_local *local = sdata->local; + enum ieee80211_band band = ieee80211_get_sdata_band(sdata); + struct ieee80211_supported_band *sband; + u8 *pos; + + sband = local->hw.wiphy->bands[band]; + if (!sband->vht_cap.vht_supported || + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT || + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 || + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10) + return 0; + + if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_vht_cap)) + return -ENOMEM; + + pos = skb_put(skb, 2 + sizeof(struct ieee80211_vht_cap)); + ieee80211_ie_build_vht_cap(pos, &sband->vht_cap, sband->vht_cap.cap); + + return 0; +} + +int mesh_add_vht_oper_ie(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_channel *channel; + struct ieee80211_supported_band *sband; + struct ieee80211_sta_vht_cap *vht_cap; + u8 *pos; + + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (WARN_ON(!chanctx_conf)) { + rcu_read_unlock(); + return -EINVAL; + } + channel = chanctx_conf->def.chan; + rcu_read_unlock(); + + sband = local->hw.wiphy->bands[channel->band]; + vht_cap = &sband->vht_cap; + + if (!vht_cap->vht_supported || + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT || + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 || + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10) + return 0; + + if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_vht_operation)) + return -ENOMEM; + + pos = skb_put(skb, 2 + sizeof(struct ieee80211_vht_operation)); + ieee80211_ie_build_vht_oper(pos, vht_cap, + &sdata->vif.bss_conf.chandef); + + return 0; +} + static void ieee80211_mesh_path_timer(unsigned long data) { struct ieee80211_sub_if_data *sdata = @@ -637,6 +703,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) 2 + ifmsh->mesh_id_len + 2 + sizeof(struct ieee80211_meshconf_ie) + 2 + sizeof(__le16) + /* awake window */ + 2 + sizeof(struct ieee80211_vht_cap) + + 2 + sizeof(struct ieee80211_vht_operation) + ifmsh->ie_len; bcn = kzalloc(sizeof(*bcn) + head_len + tail_len, GFP_KERNEL); @@ -718,6 +786,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) mesh_add_meshid_ie(sdata, skb) || mesh_add_meshconf_ie(sdata, skb) || mesh_add_awake_window_ie(sdata, skb) || + mesh_add_vht_cap_ie(sdata, skb) || + mesh_add_vht_oper_ie(sdata, skb) || mesh_add_vendor_ies(sdata, skb)) goto out_free; diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 50c8473..c60be85 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -227,6 +227,10 @@ int mesh_add_ht_cap_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); int mesh_add_ht_oper_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); +int mesh_add_vht_cap_ie(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb); +int mesh_add_vht_oper_ie(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb); void mesh_rmc_free(struct ieee80211_sub_if_data *sdata); int mesh_rmc_init(struct ieee80211_sub_if_data *sdata); void ieee80211s_init(void); diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 5838464..a360b24 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -226,6 +226,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, 2 + sizeof(struct ieee80211_meshconf_ie) + 2 + sizeof(struct ieee80211_ht_cap) + 2 + sizeof(struct ieee80211_ht_operation) + + 2 + sizeof(struct ieee80211_vht_cap) + + 2 + sizeof(struct ieee80211_vht_operation) + 2 + 8 + /* peering IE */ sdata->u.mesh.ie_len); if (!skb) @@ -306,7 +308,9 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, if (action != WLAN_SP_MESH_PEERING_CLOSE) { if (mesh_add_ht_cap_ie(sdata, skb) || - mesh_add_ht_oper_ie(sdata, skb)) + mesh_add_ht_oper_ie(sdata, skb) || + mesh_add_vht_cap_ie(sdata, skb) || + mesh_add_vht_oper_ie(sdata, skb)) goto free; } @@ -402,6 +406,9 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, elems->ht_cap_elem, sta)) changed |= IEEE80211_RC_BW_CHANGED; + ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, + elems->vht_cap_elem, sta); + if (bw != sta->sta.bandwidth) changed |= IEEE80211_RC_BW_CHANGED; -- cgit v0.10.2 From 8e0d7fe07c3f8c2a5e3b5bdbfdf09de4da2e2dd4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 28 Aug 2015 10:52:52 +0200 Subject: mac80211: remove last_beacon/ave_beacon debugfs files These file aren't really useful: - if per beacon data is required then you need to use radiotap or similar anyway, debugfs won't help much - average beacon signal is reported in station info in nl80211 and can be looked up with iw Signed-off-by: Johannes Berg diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index f1580e9..37ea30e 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -114,14 +114,6 @@ static ssize_t ieee80211_if_fmt_##name( \ return scnprintf(buf, buflen, "%pM\n", sdata->field); \ } -#define IEEE80211_IF_FMT_DEC_DIV_16(name, field) \ -static ssize_t ieee80211_if_fmt_##name( \ - const struct ieee80211_sub_if_data *sdata, \ - char *buf, int buflen) \ -{ \ - return scnprintf(buf, buflen, "%d\n", sdata->field / 16); \ -} - #define IEEE80211_IF_FMT_JIFFIES_TO_MS(name, field) \ static ssize_t ieee80211_if_fmt_##name( \ const struct ieee80211_sub_if_data *sdata, \ @@ -247,8 +239,6 @@ IEEE80211_IF_FILE_R(hw_queues); /* STA attributes */ IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC); IEEE80211_IF_FILE(aid, u.mgd.aid, DEC); -IEEE80211_IF_FILE(last_beacon, u.mgd.last_beacon_signal, DEC); -IEEE80211_IF_FILE(ave_beacon, u.mgd.ave_beacon_signal, DEC_DIV_16); IEEE80211_IF_FILE(beacon_timeout, u.mgd.beacon_timeout, JIFFIES_TO_MS); static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata, @@ -634,8 +624,6 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata) { DEBUGFS_ADD(bssid); DEBUGFS_ADD(aid); - DEBUGFS_ADD(last_beacon); - DEBUGFS_ADD(ave_beacon); DEBUGFS_ADD(beacon_timeout); DEBUGFS_ADD_MODE(smps, 0600); DEBUGFS_ADD_MODE(tkip_mic_test, 0200); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 65f4faa..9482f32 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -490,9 +490,6 @@ struct ieee80211_if_managed { s16 p2p_noa_index; - /* Signal strength from the last Beacon frame in the current BSS. */ - int last_beacon_signal; - /* * Weighted average of the signal strength from Beacon frames in the * current BSS. This is in units of 1/16 of the signal unit to maintain diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 6daadf2..ce01cd3 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3364,7 +3364,6 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, bssid = ifmgd->associated->bssid; /* Track average RSSI from the Beacon frames of the current AP */ - ifmgd->last_beacon_signal = rx_status->signal; if (ifmgd->flags & IEEE80211_STA_RESET_SIGNAL_AVE) { ifmgd->flags &= ~IEEE80211_STA_RESET_SIGNAL_AVE; ifmgd->ave_beacon_signal = rx_status->signal * 16; -- cgit v0.10.2 From 8ec6d97871f37e4743678ea4a455bd59580aa0f4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 28 Aug 2015 10:52:53 +0200 Subject: mac80211: fix driver RSSI event calculations The ifmgd->ave_beacon_signal value cannot be taken as is for comparisons, it must be divided by since it's represented like that for better accuracy of the EWMA calculations. This would lead to invalid driver RSSI events. Fix the used value. Fixes: 615f7b9bb1f8 ("mac80211: add driver RSSI threshold events") Signed-off-by: Johannes Berg diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index ce01cd3..79cfc2b 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3380,7 +3380,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, if (ifmgd->rssi_min_thold != ifmgd->rssi_max_thold && ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) { - int sig = ifmgd->ave_beacon_signal; + int sig = ifmgd->ave_beacon_signal / 16; int last_sig = ifmgd->last_ave_beacon_signal; struct ieee80211_event event = { .type = RSSI_EVENT, -- cgit v0.10.2 From 338c17ae311e6b5a439573a4043fd2d9237cd1d5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 28 Aug 2015 10:52:54 +0200 Subject: mac80211: use DECLARE_EWMA for ave_beacon_signal It doesn't seem problematic to change the weight for the average beacon signal from 3 to 4, so use DECLARE_EWMA. This also makes the code easier to maintain since bugs like the one fixed in the previous patch can't happen as easily. With a fix from Avraham Stern to invert the sign since EMWA uses unsigned values only. Signed-off-by: Johannes Berg diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 9482f32..f0aee76 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -419,6 +419,8 @@ struct ieee80211_sta_tx_tspec { bool downgraded; }; +DECLARE_EWMA(beacon_signal, 16, 4) + struct ieee80211_if_managed { struct timer_list timer; struct timer_list conn_mon_timer; @@ -490,13 +492,7 @@ struct ieee80211_if_managed { s16 p2p_noa_index; - /* - * Weighted average of the signal strength from Beacon frames in the - * current BSS. This is in units of 1/16 of the signal unit to maintain - * accuracy and to speed up calculations, i.e., the value need to be - * divided by 16 to get the actual value. - */ - int ave_beacon_signal; + struct ewma_beacon_signal ave_beacon_signal; /* * Number of Beacon frames used in ave_beacon_signal. This can be used diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 79cfc2b..c7d316b 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -82,13 +82,6 @@ MODULE_PARM_DESC(probe_wait_ms, " before disconnecting (reason 4)."); /* - * Weight given to the latest Beacon frame when calculating average signal - * strength for Beacon frames received in the current BSS. This must be - * between 1 and 15. - */ -#define IEEE80211_SIGNAL_AVE_WEIGHT 3 - -/* * How many Beacon frames need to have been used in average signal strength * before starting to indicate signal change events. */ @@ -3366,21 +3359,19 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, /* Track average RSSI from the Beacon frames of the current AP */ if (ifmgd->flags & IEEE80211_STA_RESET_SIGNAL_AVE) { ifmgd->flags &= ~IEEE80211_STA_RESET_SIGNAL_AVE; - ifmgd->ave_beacon_signal = rx_status->signal * 16; + ewma_beacon_signal_init(&ifmgd->ave_beacon_signal); ifmgd->last_cqm_event_signal = 0; ifmgd->count_beacon_signal = 1; ifmgd->last_ave_beacon_signal = 0; } else { - ifmgd->ave_beacon_signal = - (IEEE80211_SIGNAL_AVE_WEIGHT * rx_status->signal * 16 + - (16 - IEEE80211_SIGNAL_AVE_WEIGHT) * - ifmgd->ave_beacon_signal) / 16; ifmgd->count_beacon_signal++; } + ewma_beacon_signal_add(&ifmgd->ave_beacon_signal, -rx_status->signal); + if (ifmgd->rssi_min_thold != ifmgd->rssi_max_thold && ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) { - int sig = ifmgd->ave_beacon_signal / 16; + int sig = -ewma_beacon_signal_read(&ifmgd->ave_beacon_signal); int last_sig = ifmgd->last_ave_beacon_signal; struct ieee80211_event event = { .type = RSSI_EVENT, @@ -3407,10 +3398,11 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, if (bss_conf->cqm_rssi_thold && ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT && !(sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) { - int sig = ifmgd->ave_beacon_signal / 16; + int sig = -ewma_beacon_signal_read(&ifmgd->ave_beacon_signal); int last_event = ifmgd->last_cqm_event_signal; int thold = bss_conf->cqm_rssi_thold; int hyst = bss_conf->cqm_rssi_hyst; + if (sig < thold && (last_event == 0 || sig < last_event - hyst)) { ifmgd->last_cqm_event_signal = sig; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index f167056..9cabf07 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2543,7 +2543,7 @@ int ieee80211_ave_rssi(struct ieee80211_vif *vif) /* non-managed type inferfaces */ return 0; } - return ifmgd->ave_beacon_signal / 16; + return -ewma_beacon_signal_read(&ifmgd->ave_beacon_signal); } EXPORT_SYMBOL_GPL(ieee80211_ave_rssi); -- cgit v0.10.2 From 0edd5faeb07bfd3ec5402f9467e4c169dcd131e8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 28 Aug 2015 14:31:48 +0200 Subject: wireless: mark element IDs 8 and 9 reserved These were never used in the tree, and are marked as reserved in the IEEE 802.11 documentation (ANA). Signed-off-by: Johannes Berg diff --git a/drivers/net/wireless/ipw2x00/libipw_rx.c b/drivers/net/wireless/ipw2x00/libipw_rx.c index a6877dd..cef7f7d 100644 --- a/drivers/net/wireless/ipw2x00/libipw_rx.c +++ b/drivers/net/wireless/ipw2x00/libipw_rx.c @@ -1091,8 +1091,6 @@ static const char *get_info_element_string(u16 id) MFIE_STRING(TIM); MFIE_STRING(IBSS_PARAMS); MFIE_STRING(COUNTRY); - MFIE_STRING(HP_PARAMS); - MFIE_STRING(HP_TABLE); MFIE_STRING(REQUEST); MFIE_STRING(CHALLENGE); MFIE_STRING(PWR_CONSTRAINT); diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 19eb9ec..f79a02a 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1746,8 +1746,7 @@ enum ieee80211_eid { WLAN_EID_TIM = 5, WLAN_EID_IBSS_PARAMS = 6, WLAN_EID_COUNTRY = 7, - WLAN_EID_HP_PARAMS = 8, - WLAN_EID_HP_TABLE = 9, + /* 8, 9 reserved */ WLAN_EID_REQUEST = 10, WLAN_EID_QBSS_LOAD = 11, WLAN_EID_EDCA_PARAM_SET = 12, -- cgit v0.10.2 From d55d0d598e6610bbfcc1f2ecd6e8af669b94783b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Mon, 31 Aug 2015 22:59:38 +0200 Subject: nl80211: put current TX power in interface info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Many drivers implement reading current TX power (using either cfg80211 or ieee80211 op) but userspace can't get it using nl80211. Right now the only way to access it is to call some wext ioctl. Let's put TX power in interface info reply (callback is wdev specific) just like we do with current channel. To be consistent (e.g. NL80211_CMD_SET_WIPHY) let's use mBm as na unit. Signed-off-by: Rafał Miłecki Signed-off-by: Johannes Berg diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a4e6c95..50cd770 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2404,6 +2404,16 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag } } + if (rdev->ops->get_tx_power) { + int dbm, ret; + + ret = rdev_get_tx_power(rdev, wdev, &dbm); + if (ret == 0 && + nla_put_u32(msg, NL80211_ATTR_WIPHY_TX_POWER_LEVEL, + DBM_TO_MBM(dbm))) + goto nla_put_failure; + } + if (wdev->ssid_len) { if (nla_put(msg, NL80211_ATTR_SSID, wdev->ssid_len, wdev->ssid)) goto nla_put_failure; -- cgit v0.10.2 From 4dc792b8f098ab6327033fc97ba40163a2cd5fcc Mon Sep 17 00:00:00 2001 From: Helmut Schaa Date: Wed, 2 Sep 2015 13:23:30 +0200 Subject: mac80211: Split sending tx'ed frames to monitor interfaces into its own function This allows ieee80211_tx_monitor to be used directly for sending 802.11 frames to all monitor interfaces. Signed-off-by: Helmut Schaa Signed-off-by: Johannes Berg diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index f0aee76..1af655a 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1635,6 +1635,9 @@ void ieee80211_purge_tx_queue(struct ieee80211_hw *hw, struct sk_buff * ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, u32 info_flags); +void ieee80211_tx_monitor(struct ieee80211_local *local, struct sk_buff *skb, + struct ieee80211_supported_band *sband, + int retry_count, int shift, bool send_to_cooked); void ieee80211_check_fast_xmit(struct sta_info *sta); void ieee80211_check_fast_xmit_all(struct ieee80211_local *local); diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 8ba5832..98fd04c 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -668,16 +668,70 @@ void ieee80211_tx_status_noskb(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ieee80211_tx_status_noskb); -void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) +void ieee80211_tx_monitor(struct ieee80211_local *local, struct sk_buff *skb, + struct ieee80211_supported_band *sband, + int retry_count, int shift, bool send_to_cooked) { struct sk_buff *skb2; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_sub_if_data *sdata; + struct net_device *prev_dev = NULL; + int rtap_len; + + /* send frame to monitor interfaces now */ + rtap_len = ieee80211_tx_radiotap_len(info); + if (WARN_ON_ONCE(skb_headroom(skb) < rtap_len)) { + pr_err("ieee80211_tx_status: headroom too small\n"); + dev_kfree_skb(skb); + return; + } + ieee80211_add_tx_radiotap_header(local, sband, skb, retry_count, + rtap_len, shift); + + /* XXX: is this sufficient for BPF? */ + skb_set_mac_header(skb, 0); + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = htons(ETH_P_802_2); + memset(skb->cb, 0, sizeof(skb->cb)); + + rcu_read_lock(); + list_for_each_entry_rcu(sdata, &local->interfaces, list) { + if (sdata->vif.type == NL80211_IFTYPE_MONITOR) { + if (!ieee80211_sdata_running(sdata)) + continue; + + if ((sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) && + !send_to_cooked) + continue; + + if (prev_dev) { + skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2) { + skb2->dev = prev_dev; + netif_rx(skb2); + } + } + + prev_dev = sdata->dev; + } + } + if (prev_dev) { + skb->dev = prev_dev; + netif_rx(skb); + skb = NULL; + } + rcu_read_unlock(); + dev_kfree_skb(skb); +} + +void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) +{ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); __le16 fc; struct ieee80211_supported_band *sband; - struct ieee80211_sub_if_data *sdata; - struct net_device *prev_dev = NULL; struct sta_info *sta; struct rhash_head *tmp; int retry_count; @@ -685,7 +739,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) bool send_to_cooked; bool acked; struct ieee80211_bar *bar; - int rtap_len; int shift = 0; int tid = IEEE80211_NUM_TIDS; const struct bucket_table *tbl; @@ -878,51 +931,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) return; } - /* send frame to monitor interfaces now */ - rtap_len = ieee80211_tx_radiotap_len(info); - if (WARN_ON_ONCE(skb_headroom(skb) < rtap_len)) { - pr_err("ieee80211_tx_status: headroom too small\n"); - dev_kfree_skb(skb); - return; - } - ieee80211_add_tx_radiotap_header(local, sband, skb, retry_count, - rtap_len, shift); - - /* XXX: is this sufficient for BPF? */ - skb_set_mac_header(skb, 0); - skb->ip_summed = CHECKSUM_UNNECESSARY; - skb->pkt_type = PACKET_OTHERHOST; - skb->protocol = htons(ETH_P_802_2); - memset(skb->cb, 0, sizeof(skb->cb)); - - rcu_read_lock(); - list_for_each_entry_rcu(sdata, &local->interfaces, list) { - if (sdata->vif.type == NL80211_IFTYPE_MONITOR) { - if (!ieee80211_sdata_running(sdata)) - continue; - - if ((sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) && - !send_to_cooked) - continue; - - if (prev_dev) { - skb2 = skb_clone(skb, GFP_ATOMIC); - if (skb2) { - skb2->dev = prev_dev; - netif_rx(skb2); - } - } - - prev_dev = sdata->dev; - } - } - if (prev_dev) { - skb->dev = prev_dev; - netif_rx(skb); - skb = NULL; - } - rcu_read_unlock(); - dev_kfree_skb(skb); + /* send to monitor interfaces */ + ieee80211_tx_monitor(local, skb, sband, retry_count, shift, send_to_cooked); } EXPORT_SYMBOL(ieee80211_tx_status); -- cgit v0.10.2 From 5359d112dcb081fd7a5f2de287fa995f6ba0800c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 14 Sep 2015 13:56:17 +0200 Subject: Revert "mac80211: add pointer for driver use to key" This reverts commit f9a060f4b2003eb7350762e60dfc576447e44bad. No driver has turned up needing this functionality, and I've just implemented the functionality I wanted this for in a different way. Thus, remove it again, until somebody shows up with a need for having it. Signed-off-by: Johannes Berg diff --git a/include/net/mac80211.h b/include/net/mac80211.h index d62d6f4..3424ac6 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1497,10 +1497,8 @@ enum ieee80211_key_flags { * - Temporal Authenticator Rx MIC Key (64 bits) * @icv_len: The ICV length for this key type * @iv_len: The IV length for this key type - * @drv_priv: pointer for driver use */ struct ieee80211_key_conf { - void *drv_priv; atomic64_t tx_pn; u32 cipher; u8 icv_len; -- cgit v0.10.2 From 594b31ea7dc6101519deee1b31483fce2e1a7414 Mon Sep 17 00:00:00 2001 From: Frederic Danis Date: Wed, 23 Sep 2015 18:18:07 +0200 Subject: Bluetooth: Add BT_WARN and bt_dev_warn logging macros Add warning logging macros to bluetooth subsystem logs. Signed-off-by: Frederic Danis Signed-off-by: Marcel Holtmann diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index f5ade85..c4defef 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -122,11 +122,14 @@ struct bt_voice { __printf(1, 2) void bt_info(const char *fmt, ...); __printf(1, 2) +void bt_warn(const char *fmt, ...); +__printf(1, 2) void bt_err(const char *fmt, ...); __printf(1, 2) void bt_err_ratelimited(const char *fmt, ...); #define BT_INFO(fmt, ...) bt_info(fmt "\n", ##__VA_ARGS__) +#define BT_WARN(fmt, ...) bt_warn(fmt "\n", ##__VA_ARGS__) #define BT_ERR(fmt, ...) bt_err(fmt "\n", ##__VA_ARGS__) #define BT_DBG(fmt, ...) pr_debug(fmt "\n", ##__VA_ARGS__) @@ -134,6 +137,8 @@ void bt_err_ratelimited(const char *fmt, ...); #define bt_dev_info(hdev, fmt, ...) \ BT_INFO("%s: " fmt, (hdev)->name, ##__VA_ARGS__) +#define bt_dev_warn(hdev, fmt, ...) \ + BT_WARN("%s: " fmt, (hdev)->name, ##__VA_ARGS__) #define bt_dev_err(hdev, fmt, ...) \ BT_ERR("%s: " fmt, (hdev)->name, ##__VA_ARGS__) #define bt_dev_dbg(hdev, fmt, ...) \ diff --git a/net/bluetooth/lib.c b/net/bluetooth/lib.c index 8b4cdce..aa4cf64 100644 --- a/net/bluetooth/lib.c +++ b/net/bluetooth/lib.c @@ -151,6 +151,22 @@ void bt_info(const char *format, ...) } EXPORT_SYMBOL(bt_info); +void bt_warn(const char *format, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, format); + + vaf.fmt = format; + vaf.va = &args; + + pr_warn("%pV", &vaf); + + va_end(args); +} +EXPORT_SYMBOL(bt_warn); + void bt_err(const char *format, ...) { struct va_format vaf; -- cgit v0.10.2 From 5cebdfea32b89911d4540440c1c2854a1a3d591e Mon Sep 17 00:00:00 2001 From: Frederic Danis Date: Wed, 23 Sep 2015 18:18:08 +0200 Subject: Bluetooth: hci_bcm: Fix IRQ polarity for T100 ACPI table for BCM2E39 of T100TA is not correct. Set correct irq_polarity for this device. Signed-off-by: Frederic Danis Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index f306541..2cb5089 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -513,6 +514,22 @@ static const struct acpi_gpio_mapping acpi_bcm_default_gpios[] = { }; #ifdef CONFIG_ACPI +static u8 acpi_active_low = ACPI_ACTIVE_LOW; + +/* IRQ polarity of some chipsets are not defined correctly in ACPI table. */ +static const struct dmi_system_id bcm_wrong_irq_dmi_table[] = { + { + .ident = "Asus T100TA", + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, + "ASUSTeK COMPUTER INC."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"), + }, + .driver_data = &acpi_active_low, + }, + { } +}; + static int bcm_resource(struct acpi_resource *ares, void *data) { struct bcm_device *dev = data; @@ -552,6 +569,7 @@ static int bcm_acpi_probe(struct bcm_device *dev) const struct acpi_device_id *id; struct acpi_device *adev; LIST_HEAD(resources); + const struct dmi_system_id *dmi_id; int ret; id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev); @@ -608,6 +626,13 @@ static int bcm_acpi_probe(struct bcm_device *dev) acpi_dev_get_resources(adev, &resources, bcm_resource, dev); + dmi_id = dmi_first_match(bcm_wrong_irq_dmi_table); + if (dmi_id) { + bt_dev_warn(dev, "%s: Overwriting IRQ polarity to active low", + dmi_id->ident); + dev->irq_polarity = *(u8 *)dmi_id->driver_data; + } + return 0; } #else -- cgit v0.10.2 From b7a622a249736b36c0bf4c3f986ed431281d5e98 Mon Sep 17 00:00:00 2001 From: Frederic Danis Date: Wed, 23 Sep 2015 18:18:09 +0200 Subject: Bluetooth: hci_bcm: Prepare PM runtime support Change some CONFIG_PM_SLEEP to CONFIG_PM as hu and is_suspended parameters will be used during PM runtime callbacks. Add bcm_suspend_device() and bcm_resume_device() which performs link management for PM callbacks. These functions will be used for runtime management. Signed-off-by: Frederic Danis Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 2cb5089..fc5ca95 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -56,7 +56,7 @@ struct bcm_device { int irq; u8 irq_polarity; -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM struct hci_uart *hu; bool is_suspended; /* suspend/resume flag */ #endif @@ -153,7 +153,7 @@ static int bcm_gpio_set_power(struct bcm_device *dev, bool powered) return 0; } -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM static irqreturn_t bcm_host_wake(int irq, void *data) { struct bcm_device *bdev = data; @@ -259,7 +259,7 @@ static int bcm_open(struct hci_uart *hu) if (hu->tty->dev->parent == dev->pdev->dev.parent) { bcm->dev = dev; hu->init_speed = dev->init_speed; -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM dev->hu = hu; #endif bcm_gpio_set_power(bcm->dev, true); @@ -283,7 +283,7 @@ static int bcm_close(struct hci_uart *hu) mutex_lock(&bcm_device_lock); if (bcm_device_exists(bdev)) { bcm_gpio_set_power(bdev, false); -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM if (device_can_wakeup(&bdev->pdev->dev)) { devm_free_irq(&bdev->pdev->dev, bdev->irq, bdev); device_init_wakeup(&bdev->pdev->dev, false); @@ -425,24 +425,17 @@ static struct sk_buff *bcm_dequeue(struct hci_uart *hu) return skb_dequeue(&bcm->txq); } -#ifdef CONFIG_PM_SLEEP -/* Platform suspend callback */ -static int bcm_suspend(struct device *dev) +#ifdef CONFIG_PM +static int bcm_suspend_device(struct device *dev) { struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev)); - int error; - bt_dev_dbg(bdev, "suspend: is_suspended %d", bdev->is_suspended); - - mutex_lock(&bcm_device_lock); - - if (!bdev->hu) - goto unlock; + bt_dev_dbg(bdev, ""); - if (!bdev->is_suspended) { + if (!bdev->is_suspended && bdev->hu) { hci_uart_set_flow_control(bdev->hu, true); - /* Once this callback returns, driver suspends BT via GPIO */ + /* Once this returns, driver suspends BT via GPIO */ bdev->is_suspended = true; } @@ -453,6 +446,52 @@ static int bcm_suspend(struct device *dev) mdelay(15); } + return 0; +} + +static int bcm_resume_device(struct device *dev) +{ + struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev)); + + bt_dev_dbg(bdev, ""); + + if (bdev->device_wakeup) { + gpiod_set_value(bdev->device_wakeup, true); + bt_dev_dbg(bdev, "resume, delaying 15 ms"); + mdelay(15); + } + + /* When this executes, the device has woken up already */ + if (bdev->is_suspended && bdev->hu) { + bdev->is_suspended = false; + + hci_uart_set_flow_control(bdev->hu, false); + } + + return 0; +} +#endif + +#ifdef CONFIG_PM_SLEEP +/* Platform suspend callback */ +static int bcm_suspend(struct device *dev) +{ + struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev)); + int error; + + bt_dev_dbg(bdev, "suspend: is_suspended %d", bdev->is_suspended); + + /* bcm_suspend can be called at any time as long as platform device is + * bound, so it should use bcm_device_lock to protect access to hci_uart + * and device_wake-up GPIO. + */ + mutex_lock(&bcm_device_lock); + + if (!bdev->hu) + goto unlock; + + bcm_suspend_device(dev); + if (device_may_wakeup(&bdev->pdev->dev)) { error = enable_irq_wake(bdev->irq); if (!error) @@ -472,6 +511,10 @@ static int bcm_resume(struct device *dev) bt_dev_dbg(bdev, "resume: is_suspended %d", bdev->is_suspended); + /* bcm_resume can be called at any time as long as platform device is + * bound, so it should use bcm_device_lock to protect access to hci_uart + * and device_wake-up GPIO. + */ mutex_lock(&bcm_device_lock); if (!bdev->hu) @@ -482,18 +525,7 @@ static int bcm_resume(struct device *dev) bt_dev_dbg(bdev, "BCM irq: disabled"); } - if (bdev->device_wakeup) { - gpiod_set_value(bdev->device_wakeup, true); - bt_dev_dbg(bdev, "resume, delaying 15 ms"); - mdelay(15); - } - - /* When this callback executes, the device has woken up already */ - if (bdev->is_suspended) { - bdev->is_suspended = false; - - hci_uart_set_flow_control(bdev->hu, false); - } + bcm_resume_device(dev); unlock: mutex_unlock(&bcm_device_lock); -- cgit v0.10.2 From 7649faff1cfe4f76dabf78cd53d659d39f65b3c1 Mon Sep 17 00:00:00 2001 From: Frederic Danis Date: Wed, 23 Sep 2015 18:18:10 +0200 Subject: Bluetooth: Remove useless rx_lock spinlock rx_lock spinlock is only used in hci_uart_tty_receive() which is the receive_buf ldisc callback. hci_uart_tty_receive() is protected from re-entrance by its only caller (flush_to_ldisc() in drivers/tty/tty_buffer.c) which held a mutex (buf->lock) for this section. This lock allows "safe use of the line discipline's receive_buf() method by excluding the buffer work and any pending flush from using the flip buffer." (comments from tty_buffer_lock_exclusive() in drivers/tty/tty_buffer.c) So, no need to double protect this resource with rx_lock. Signed-off-by: Frederic Danis Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 0d5a05a..ad66c69 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -470,8 +470,6 @@ static int hci_uart_tty_open(struct tty_struct *tty) INIT_WORK(&hu->init_ready, hci_uart_init_work); INIT_WORK(&hu->write_work, hci_uart_write_work); - spin_lock_init(&hu->rx_lock); - /* Flush any pending characters in the driver and line discipline. */ /* FIXME: why is this needed. Note don't use ldisc_ref here as the @@ -569,14 +567,14 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, if (!test_bit(HCI_UART_PROTO_SET, &hu->flags)) return; - spin_lock(&hu->rx_lock); + /* It does not need a lock here as it is already protected by a mutex in + * tty caller + */ hu->proto->recv(hu, data, count); if (hu->hdev) hu->hdev->stat.byte_rx += count; - spin_unlock(&hu->rx_lock); - tty_unthrottle(tty); } diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index 495b9ef..2f7bb35 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -85,7 +85,6 @@ struct hci_uart { struct sk_buff *tx_skb; unsigned long tx_state; - spinlock_t rx_lock; unsigned int init_speed; unsigned int oper_speed; -- cgit v0.10.2 From e88ab30d3669f08e94e66e7f926713be93af97fc Mon Sep 17 00:00:00 2001 From: Frederic Danis Date: Wed, 23 Sep 2015 18:18:11 +0200 Subject: Bluetooth: hci_bcm: Add suspend/resume runtime PM functions Adds autosuspend runtime functionality to BCM UART driver. Autosuspend is enabled at end of bcm_setup. bcm_device_lock is used for system sleep functions as they can be called at any time. bcm_device_lock is not held for runtime suspend functions as this is only enabled as long as platform device is opened. Signed-off-by: Frederic Danis Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index fc5ca95..0c791ac 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -40,6 +41,8 @@ #include "btbcm.h" #include "hci_uart.h" +#define BCM_AUTOSUSPEND_DELAY 5000 /* default autosleep delay */ + struct bcm_device { struct list_head list; @@ -160,6 +163,10 @@ static irqreturn_t bcm_host_wake(int irq, void *data) bt_dev_dbg(bdev, "Host wake IRQ"); + pm_runtime_get(&bdev->pdev->dev); + pm_runtime_mark_last_busy(&bdev->pdev->dev); + pm_runtime_put_autosuspend(&bdev->pdev->dev); + return IRQ_HANDLED; } @@ -183,6 +190,12 @@ static int bcm_request_irq(struct bcm_data *bcm) goto unlock; device_init_wakeup(&bdev->pdev->dev, true); + + pm_runtime_set_autosuspend_delay(&bdev->pdev->dev, + BCM_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(&bdev->pdev->dev); + pm_runtime_set_active(&bdev->pdev->dev); + pm_runtime_enable(&bdev->pdev->dev); } unlock: @@ -198,7 +211,7 @@ static const struct bcm_set_sleep_mode default_sleep_params = { .bt_wake_active = 1, /* BT_WAKE active mode: 1 = high, 0 = low */ .host_wake_active = 0, /* HOST_WAKE active mode: 1 = high, 0 = low */ .allow_host_sleep = 1, /* Allow host sleep in SCO flag */ - .combine_modes = 0, /* Combine sleep and LPM flag */ + .combine_modes = 1, /* Combine sleep and LPM flag */ .tristate_control = 0, /* Allow tri-state control of UART tx flag */ /* Irrelevant USB flags */ .usb_auto_sleep = 0, @@ -284,6 +297,9 @@ static int bcm_close(struct hci_uart *hu) if (bcm_device_exists(bdev)) { bcm_gpio_set_power(bdev, false); #ifdef CONFIG_PM + pm_runtime_disable(&bdev->pdev->dev); + pm_runtime_set_suspended(&bdev->pdev->dev); + if (device_can_wakeup(&bdev->pdev->dev)) { devm_free_irq(&bdev->pdev->dev, bdev->irq, bdev); device_init_wakeup(&bdev->pdev->dev, false); @@ -400,6 +416,15 @@ static int bcm_recv(struct hci_uart *hu, const void *data, int count) bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err); bcm->rx_skb = NULL; return err; + } else if (!bcm->rx_skb) { + /* Delay auto-suspend when receiving completed packet */ + mutex_lock(&bcm_device_lock); + if (bcm->dev && bcm_device_exists(bcm->dev)) { + pm_runtime_get(&bcm->dev->pdev->dev); + pm_runtime_mark_last_busy(&bcm->dev->pdev->dev); + pm_runtime_put_autosuspend(&bcm->dev->pdev->dev); + } + mutex_unlock(&bcm_device_lock); } return count; @@ -421,8 +446,27 @@ static int bcm_enqueue(struct hci_uart *hu, struct sk_buff *skb) static struct sk_buff *bcm_dequeue(struct hci_uart *hu) { struct bcm_data *bcm = hu->priv; + struct sk_buff *skb = NULL; + struct bcm_device *bdev = NULL; + + mutex_lock(&bcm_device_lock); + + if (bcm_device_exists(bcm->dev)) { + bdev = bcm->dev; + pm_runtime_get_sync(&bdev->pdev->dev); + /* Shall be resumed here */ + } + + skb = skb_dequeue(&bcm->txq); + + if (bdev) { + pm_runtime_mark_last_busy(&bdev->pdev->dev); + pm_runtime_put_autosuspend(&bdev->pdev->dev); + } - return skb_dequeue(&bcm->txq); + mutex_unlock(&bcm_device_lock); + + return skb; } #ifdef CONFIG_PM @@ -490,7 +534,8 @@ static int bcm_suspend(struct device *dev) if (!bdev->hu) goto unlock; - bcm_suspend_device(dev); + if (pm_runtime_active(dev)) + bcm_suspend_device(dev); if (device_may_wakeup(&bdev->pdev->dev)) { error = enable_irq_wake(bdev->irq); @@ -530,6 +575,10 @@ static int bcm_resume(struct device *dev) unlock: mutex_unlock(&bcm_device_lock); + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + return 0; } #endif @@ -750,7 +799,10 @@ MODULE_DEVICE_TABLE(acpi, bcm_acpi_match); #endif /* Platform suspend and resume callbacks */ -static SIMPLE_DEV_PM_OPS(bcm_pm_ops, bcm_suspend, bcm_resume); +static const struct dev_pm_ops bcm_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(bcm_suspend, bcm_resume) + SET_RUNTIME_PM_OPS(bcm_suspend_device, bcm_resume_device, NULL) +}; static struct platform_driver bcm_driver = { .probe = bcm_probe, -- cgit v0.10.2 From eb24d061f4b80338672b7d38ee6331896f0a7710 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Thu, 24 Sep 2015 19:40:33 +0200 Subject: mrf24j40: remove trailing semicolon This patch removes a trailing semicolon which was introduced by commit d3f1bc3 ("mrf24j40: add cca mode support") and reported by kbuild test robot. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index 2770df1..aca0fb3 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -1239,7 +1239,7 @@ static void mrf24j40_phy_setup(struct mrf24j40 *devrec) devrec->hw->phy->supported.min_maxbe = 5; devrec->hw->phy->supported.max_maxbe = 5; - devrec->hw->phy->cca.mode = NL802154_CCA_CARRIER;; + devrec->hw->phy->cca.mode = NL802154_CCA_CARRIER; devrec->hw->phy->supported.cca_modes = BIT(NL802154_CCA_ENERGY) | BIT(NL802154_CCA_CARRIER) | BIT(NL802154_CCA_ENERGY_CARRIER); -- cgit v0.10.2 From f856f21dbcd162a53e30987a91d75d5ab54a7f80 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Thu, 24 Sep 2015 09:37:10 +0200 Subject: ieee802154: remove unnecessary includes This patch removes some unnecessary includes from ieee802154 header, which was introduced by commit b609fb54adfa ("ieee802154: add helpers for frame control checks"). Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/include/linux/ieee802154.h b/include/linux/ieee802154.h index aca228b..d3e4156 100644 --- a/include/linux/ieee802154.h +++ b/include/linux/ieee802154.h @@ -25,9 +25,6 @@ #include #include -#include -#include -#include #define IEEE802154_MTU 127 #define IEEE802154_ACK_PSDU_LEN 5 -- cgit v0.10.2 From c2d5ecfaeafdedfb997a466c654c7029c511f43d Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Thu, 24 Sep 2015 09:37:11 +0200 Subject: mac802154: iface: assume big endian for af_packet The callback "create" and "parse" from header_ops are called from netdev core upper-layer functionality, like af_packet. These callbacks assumes big endian for addresses and we should not introduce a special byteordering handling for ieee802154 over af_packet in userspace. We have an identical issue with setting the mac address which also assumes big endian byteordering. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c index b5a0936..3954bcf 100644 --- a/net/mac802154/iface.c +++ b/net/mac802154/iface.c @@ -461,7 +461,7 @@ static int mac802154_header_create(struct sk_buff *skb, hdr.dest.pan_id = wpan_dev->pan_id; hdr.dest.mode = IEEE802154_ADDR_LONG; - memcpy(&hdr.dest.extended_addr, daddr, IEEE802154_EXTENDED_ADDR_LEN); + ieee802154_be64_to_le64(&hdr.dest.extended_addr, daddr); hdr.source.pan_id = hdr.dest.pan_id; hdr.source.mode = IEEE802154_ADDR_LONG; @@ -469,8 +469,7 @@ static int mac802154_header_create(struct sk_buff *skb, if (!saddr) hdr.source.extended_addr = wpan_dev->extended_addr; else - memcpy(&hdr.source.extended_addr, saddr, - IEEE802154_EXTENDED_ADDR_LEN); + ieee802154_be64_to_le64(&hdr.source.extended_addr, saddr); hlen = ieee802154_hdr_push(skb, &hdr); if (hlen < 0) @@ -496,8 +495,7 @@ mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr) } if (hdr.source.mode == IEEE802154_ADDR_LONG) { - memcpy(haddr, &hdr.source.extended_addr, - IEEE802154_EXTENDED_ADDR_LEN); + ieee802154_le64_to_be64(haddr, &hdr.source.extended_addr); return IEEE802154_EXTENDED_ADDR_LEN; } -- cgit v0.10.2 From 54b5b34eded2f068e8db181dec78be07b288dcfc Mon Sep 17 00:00:00 2001 From: Prasanna Karthik Date: Fri, 25 Sep 2015 07:59:18 +0000 Subject: Bluetooth: bluecard: Comparison to NULL could be re-written replaced 'not null' comparison that is readable, reported by checkpatch. Signed-off-by: Prasanna Karthik Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index 35e63aa..25b7166 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -390,7 +390,7 @@ static void bluecard_receive(struct bluecard_info *info, for (i = 0; i < len; i++) { /* Allocate packet */ - if (info->rx_skb == NULL) { + if (!info->rx_skb) { info->rx_state = RECV_WAIT_PACKET_TYPE; info->rx_count = 0; info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); -- cgit v0.10.2 From 7a1dc788e5c754685e3491c637875d8368bea42d Mon Sep 17 00:00:00 2001 From: Prasanna Karthik Date: Fri, 25 Sep 2015 09:02:18 +0000 Subject: Bluetooth: bt3c_cs: Comparison to NULL re-written NOT NULL comparison modified to be readable, reported by checkpatch. Signed-off-by: Prasanna Karthik Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index 772a277..b8f4b63 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -233,7 +233,7 @@ static void bt3c_receive(struct bt3c_info *info) info->hdev->stat.byte_rx++; /* Allocate packet */ - if (info->rx_skb == NULL) { + if (!info->rx_skb) { info->rx_state = RECV_WAIT_PACKET_TYPE; info->rx_count = 0; info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); -- cgit v0.10.2 From 05bd7762faac386c224b159607fdaf65e5760672 Mon Sep 17 00:00:00 2001 From: Prasanna Karthik Date: Fri, 25 Sep 2015 09:05:04 +0000 Subject: Bluetooth: btuart_cs: Comparison to NULL re-written NOT NULL comparison modified to be readable, reported by checkpatch. Signed-off-by: Prasanna Karthik Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index abb4d21..dfaa1c9 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -188,7 +188,7 @@ static void btuart_receive(struct btuart_info *info) info->hdev->stat.byte_rx++; /* Allocate packet */ - if (info->rx_skb == NULL) { + if (!info->rx_skb) { info->rx_state = RECV_WAIT_PACKET_TYPE; info->rx_count = 0; info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); -- cgit v0.10.2 From 1f438c6138ac5739ce16c8db04689e438d65dfe6 Mon Sep 17 00:00:00 2001 From: Prasanna Karthik Date: Fri, 25 Sep 2015 09:03:14 +0000 Subject: Bluetooth: btmrvl: Comparison to NULL re-written NOT NULL comparison modified to be readable, reported by checkpatch. Signed-off-by: Prasanna Karthik Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index bc110f6..050ad6b 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -184,7 +184,7 @@ static int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 opcode, } skb = bt_skb_alloc(HCI_COMMAND_HDR_SIZE + len, GFP_ATOMIC); - if (skb == NULL) { + if (!skb) { BT_ERR("No free skb"); return -ENOMEM; } diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index 7f6fb17..71ea2a3 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -654,7 +654,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv) /* Allocate buffer */ skb = bt_skb_alloc(num_blocks * blksz + BTSDIO_DMA_ALIGN, GFP_ATOMIC); - if (skb == NULL) { + if (!skb) { BT_ERR("No free skb"); ret = -ENOMEM; goto exit; -- cgit v0.10.2 From b0b252278082787eec54098556566d4d68b8126c Mon Sep 17 00:00:00 2001 From: Peter Oh Date: Thu, 17 Sep 2015 14:29:07 +0300 Subject: ath: fix incorrect PPB on JAPAN chirp radar The number of pulses per burst on Japan chirp radar is between 1 and 3. The previous value, 20, is representing number of bursts, but since current DFS detector is using pulse detection other than bursts, use the pulse number for correct radar detection. Also using the highest number helps to avoid false detection. Signed-off-by: Peter Oh Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.c b/drivers/net/wireless/ath/dfs_pattern_detector.c index 656ce42..5aa053a 100644 --- a/drivers/net/wireless/ath/dfs_pattern_detector.c +++ b/drivers/net/wireless/ath/dfs_pattern_detector.c @@ -121,7 +121,7 @@ static const struct radar_detector_specs jp_radar_ref_types[] = { JP_PATTERN(4, 0, 5, 150, 230, 1, 23, 50, false), JP_PATTERN(5, 6, 10, 200, 500, 1, 16, 50, false), JP_PATTERN(6, 11, 20, 200, 500, 1, 12, 50, false), - JP_PATTERN(7, 50, 100, 1000, 2000, 1, 20, 50, false), + JP_PATTERN(7, 50, 100, 1000, 2000, 1, 3, 50, false), JP_PATTERN(5, 0, 1, 333, 333, 1, 9, 50, false), }; -- cgit v0.10.2 From 44acedb00b6d4b56ddab04362ccfa133b0d3b013 Mon Sep 17 00:00:00 2001 From: Peter Oh Date: Thu, 17 Sep 2015 14:29:08 +0300 Subject: ath: use PRI value given by spec for fixed PRI PRI value is used as divider when DFS detector analyzes candidate radar pulses. If PRI deviation is big from its origin PRI, DFS detector could miss valid radar reports since HW often misses detecting radar pulses and causes long interval value of pulses. For instance from practical results, if runtime PRI is calculated as 1431 for fixed PRI value of 1428 and delta timestamp logs 15719, the modular remainder will be 1409 and the delta between the remainder and runtime PRI is 22 that is bigger than PRI tolerance which is 16. As a result this radar report will be ignored even though it's valid. By using spec defined PRI for fixed PRI, we can correct this error. Signed-off-by: Peter Oh Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.c b/drivers/net/wireless/ath/dfs_pattern_detector.c index 5aa053a..9d68712 100644 --- a/drivers/net/wireless/ath/dfs_pattern_detector.c +++ b/drivers/net/wireless/ath/dfs_pattern_detector.c @@ -21,12 +21,6 @@ #include "dfs_pri_detector.h" #include "ath.h" -/* - * tolerated deviation of radar time stamp in usecs on both sides - * TODO: this might need to be HW-dependent - */ -#define PRI_TOLERANCE 16 - /** * struct radar_types - contains array of patterns defined for one DFS domain * @domain: DFS regulatory domain diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.h b/drivers/net/wireless/ath/dfs_pattern_detector.h index 25a43d6..92be353 100644 --- a/drivers/net/wireless/ath/dfs_pattern_detector.h +++ b/drivers/net/wireless/ath/dfs_pattern_detector.h @@ -21,6 +21,11 @@ #include #include +/* tolerated deviation of radar time stamp in usecs on both sides + * TODO: this might need to be HW-dependent + */ +#define PRI_TOLERANCE 16 + /** * struct ath_dfs_pool_stats - DFS Statistics for global pools */ diff --git a/drivers/net/wireless/ath/dfs_pri_detector.c b/drivers/net/wireless/ath/dfs_pri_detector.c index cc5c592..05b0464 100644 --- a/drivers/net/wireless/ath/dfs_pri_detector.c +++ b/drivers/net/wireless/ath/dfs_pri_detector.c @@ -25,6 +25,9 @@ struct ath_dfs_pool_stats global_dfs_pool_stats = {}; #define DFS_POOL_STAT_INC(c) (global_dfs_pool_stats.c++) #define DFS_POOL_STAT_DEC(c) (global_dfs_pool_stats.c--) +#define GET_PRI_TO_USE(MIN, MAX, RUNTIME) \ + (MIN + PRI_TOLERANCE == MAX - PRI_TOLERANCE ? \ + MIN + PRI_TOLERANCE : RUNTIME) /** * struct pulse_elem - elements in pulse queue @@ -243,7 +246,8 @@ static bool pseq_handler_create_sequences(struct pri_detector *pde, ps.count_falses = 0; ps.first_ts = p->ts; ps.last_ts = ts; - ps.pri = ts - p->ts; + ps.pri = GET_PRI_TO_USE(pde->rs->pri_min, + pde->rs->pri_max, ts - p->ts); ps.dur = ps.pri * (pde->rs->ppb - 1) + 2 * pde->rs->max_pri_tolerance; -- cgit v0.10.2 From bd4a41e6dee43b5b00876e37d42abffa298d63fe Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Wed, 16 Sep 2015 13:19:00 +0530 Subject: ath10k: fix ldpc param for fixed rate ldpc is not configured for fixed rates. This blocks auto rate vs fixed rate performance comparison. Since firmware is considering ldpc vdev param for fixed rate selection, it has to be configured to enable ldpc for fixed rates. Signed-off-by: Rajkumar Manoharan Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index bb44161..3f6c1c4 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -5957,7 +5957,7 @@ ath10k_mac_bitrate_mask_get_single_rate(struct ath10k *ar, } static int ath10k_mac_set_fixed_rate_params(struct ath10k_vif *arvif, - u8 rate, u8 nss, u8 sgi) + u8 rate, u8 nss, u8 sgi, u8 ldpc) { struct ath10k *ar = arvif->ar; u32 vdev_param; @@ -5990,6 +5990,13 @@ static int ath10k_mac_set_fixed_rate_params(struct ath10k_vif *arvif, return ret; } + vdev_param = ar->wmi.vdev_param->ldpc; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, ldpc); + if (ret) { + ath10k_warn(ar, "failed to set ldpc param %d: %d\n", ldpc, ret); + return ret; + } + return 0; } @@ -6053,6 +6060,7 @@ static int ath10k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, u8 rate; u8 nss; u8 sgi; + u8 ldpc; int single_nss; int ret; @@ -6062,6 +6070,7 @@ static int ath10k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, band = def.chan->band; ht_mcs_mask = mask->control[band].ht_mcs; vht_mcs_mask = mask->control[band].vht_mcs; + ldpc = !!(ar->ht_cap_info & WMI_HT_CAP_LDPC); sgi = mask->control[band].gi; if (sgi == NL80211_TXRATE_FORCE_LGI) @@ -6100,7 +6109,7 @@ static int ath10k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, mutex_lock(&ar->conf_mutex); - ret = ath10k_mac_set_fixed_rate_params(arvif, rate, nss, sgi); + ret = ath10k_mac_set_fixed_rate_params(arvif, rate, nss, sgi, ldpc); if (ret) { ath10k_warn(ar, "failed to set fixed rate params on vdev %i: %d\n", arvif->vdev_id, ret); -- cgit v0.10.2 From b8402d827fbb288f4ecee585e151bab5a81b3c58 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Thu, 17 Sep 2015 08:17:33 +0200 Subject: ath10k: fix MSI-X registering for qca99x0 In case of qca99x0 and MSI-X supported/enabled we failed during interrupts registering with message: ath10k_pci 0000:04:00.0: failed to request MSI-X ce irq 50: -22 Issue/fix was reproduced/tested using Dell Latitude E6430 laptop. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 23afcda..bc421a5 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -414,16 +414,6 @@ enum ath10k_hw_rate_cck { #define CE_COUNT ar->hw_values->ce_count /* - * Total number of PCIe MSI interrupts requested for all interrupt sources. - * PCIe standard forces this to be a power of 2. - * Some Host OS's limit MSI requests that can be granted to 8 - * so for now we abide by this limit and avoid requesting more - * than that. - */ -#define MSI_NUM_REQUEST_LOG2 3 -#define MSI_NUM_REQUEST (1<num_msi_intrs = MSI_NUM_REQUEST; + ar_pci->num_msi_intrs = MSI_ASSIGN_CE_MAX + 1; ret = pci_enable_msi_range(ar_pci->pdev, ar_pci->num_msi_intrs, ar_pci->num_msi_intrs); if (ret > 0) @@ -2705,18 +2702,13 @@ static int ath10k_pci_deinit_irq(struct ath10k *ar) switch (ar_pci->num_msi_intrs) { case 0: ath10k_pci_deinit_irq_legacy(ar); - return 0; - case 1: - /* fall-through */ - case MSI_NUM_REQUEST: - pci_disable_msi(ar_pci->pdev); - return 0; + break; default: pci_disable_msi(ar_pci->pdev); + break; } - ath10k_warn(ar, "unknown irq configuration upon deinit\n"); - return -EINVAL; + return 0; } static int ath10k_pci_wait_for_target_init(struct ath10k *ar) -- cgit v0.10.2 From a913718812d5998b3af2b6d6b5f0be4a09caebb1 Mon Sep 17 00:00:00 2001 From: Prasanna Karthik Date: Mon, 28 Sep 2015 08:03:24 +0000 Subject: Bluetooth: hci_qca: Changed unsigned long to bool 'retransmit' being set in HCI_IBS_TX_WAKING case, using bool would be efficient. Initialize local bool to false. Signed-off-by: Prasanna Karthik Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 21f4ea4..b4a0393 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -347,7 +347,7 @@ static void hci_ibs_wake_retrans_timeout(unsigned long arg) struct hci_uart *hu = (struct hci_uart *)arg; struct qca_data *qca = hu->priv; unsigned long flags, retrans_delay; - unsigned long retransmit = 0; + bool retransmit = false; BT_DBG("hu %p wake retransmit timeout in %d state", hu, qca->tx_ibs_state); @@ -358,7 +358,7 @@ static void hci_ibs_wake_retrans_timeout(unsigned long arg) switch (qca->tx_ibs_state) { case HCI_IBS_TX_WAKING: /* No WAKE_ACK, retransmit WAKE */ - retransmit = 1; + retransmit = true; if (send_hci_ibs_cmd(HCI_IBS_WAKE_IND, hu) < 0) { BT_ERR("Failed to acknowledge device wake up"); break; -- cgit v0.10.2 From 75effb03ee8e4c9d4bbc909118ce5444b047dfde Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 26 Aug 2015 22:14:53 +0200 Subject: brcmfmac: consolidate ifp lookup in driver core In rx path the firmware provide an interface index which is used to map to a struct brcmf_if instance. However, this involves some trick that is done in two places. This is changed by having driver core providing brcmf_get_ifp() function. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c index 8e0e91c..83aa6a0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c @@ -276,6 +276,7 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, struct sk_buff *pktbuf) { struct brcmf_proto_bcdc_header *h; + struct brcmf_if *ifp; brcmf_dbg(BCDC, "Enter\n"); @@ -289,30 +290,21 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, trace_brcmf_bcdchdr(pktbuf->data); h = (struct brcmf_proto_bcdc_header *)(pktbuf->data); - *ifidx = BCDC_GET_IF_IDX(h); - if (*ifidx >= BRCMF_MAX_IFS) { - brcmf_err("rx data ifnum out of range (%d)\n", *ifidx); + ifp = brcmf_get_ifp(drvr, BCDC_GET_IF_IDX(h)); + if (IS_ERR_OR_NULL(ifp)) { + brcmf_dbg(INFO, "no matching ifp found\n"); return -EBADE; } - /* The ifidx is the idx to map to matching netdev/ifp. When receiving - * events this is easy because it contains the bssidx which maps - * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd. - * bssidx 1 is used for p2p0 and no data can be received or - * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0 - */ - if (*ifidx) - (*ifidx)++; - if (((h->flags & BCDC_FLAG_VER_MASK) >> BCDC_FLAG_VER_SHIFT) != BCDC_PROTO_VER) { brcmf_err("%s: non-BCDC packet received, flags 0x%x\n", - brcmf_ifname(drvr, *ifidx), h->flags); + brcmf_ifname(drvr, ifp->ifidx), h->flags); return -EBADE; } if (h->flags & BCDC_FLAG_SUM_GOOD) { brcmf_dbg(BCDC, "%s: BDC rcv, good checksum, flags 0x%x\n", - brcmf_ifname(drvr, *ifidx), h->flags); + brcmf_ifname(drvr, ifp->ifidx), h->flags); pktbuf->ip_summed = CHECKSUM_UNNECESSARY; } @@ -320,12 +312,15 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, skb_pull(pktbuf, BCDC_HEADER_LEN); if (do_fws) - brcmf_fws_hdrpull(drvr, *ifidx, h->data_offset << 2, pktbuf); + brcmf_fws_hdrpull(drvr, ifp->ifidx, h->data_offset << 2, + pktbuf); else skb_pull(pktbuf, h->data_offset << 2); if (pktbuf->len == 0) return -ENODATA; + + *ifidx = ifp->ifidx; return 0; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index fe9d3fb..1747d59 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -83,6 +83,25 @@ char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx) return ""; } +struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx) +{ + if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) { + brcmf_err("ifidx %d out of range\n", ifidx); + return ERR_PTR(-ERANGE); + } + + /* The ifidx is the idx to map to matching netdev/ifp. When receiving + * events this is easy because it contains the bssidx which maps + * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd. + * bssidx 1 is used for p2p0 and no data can be received or + * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0 + */ + if (ifidx) + ifidx++; + + return drvr->iflist[ifidx]; +} + static void _brcmf_set_multicast_list(struct work_struct *work) { struct brcmf_if *ifp; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.h b/drivers/net/wireless/brcm80211/brcmfmac/core.h index 7463041..1dc5b96 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h @@ -202,7 +202,7 @@ int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp); /* Return pointer to interface name */ char *brcmf_ifname(struct brcmf_pub *drvr, int idx); - +struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx); int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked); struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, char *name, u8 *mac_addr); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 7b2136c..1271d1d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -1081,16 +1081,8 @@ brcmf_msgbuf_rx_skb(struct brcmf_msgbuf *msgbuf, struct sk_buff *skb, { struct brcmf_if *ifp; - /* The ifidx is the idx to map to matching netdev/ifp. When receiving - * events this is easy because it contains the bssidx which maps - * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd. - * bssidx 1 is used for p2p0 and no data can be received or - * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0 - */ - if (ifidx) - (ifidx)++; - ifp = msgbuf->drvr->iflist[ifidx]; - if (!ifp || !ifp->ndev) { + ifp = brcmf_get_ifp(msgbuf->drvr, ifidx); + if (IS_ERR_OR_NULL(ifp) || !ifp->ndev) { brcmf_err("Received pkt for invalid ifidx %d\n", ifidx); brcmu_pkt_buf_free_skb(skb); return; -- cgit v0.10.2 From 796cfb65e3ed01a9b08e3a0b93e34120c54bbbd2 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 26 Aug 2015 22:14:54 +0200 Subject: brcmfmac: make brcmf_proto_hdrpull() return struct brcmf_if instance Avoid spreading the ifidx in the driver, but have it return the struct brcmf_if instance. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c index 83aa6a0..aa5ed65 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c @@ -272,11 +272,11 @@ brcmf_proto_bcdc_hdrpush(struct brcmf_pub *drvr, int ifidx, u8 offset, } static int -brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, - struct sk_buff *pktbuf) +brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, + struct sk_buff *pktbuf, struct brcmf_if **ifp) { struct brcmf_proto_bcdc_header *h; - struct brcmf_if *ifp; + struct brcmf_if *tmp_if; brcmf_dbg(BCDC, "Enter\n"); @@ -290,21 +290,21 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, trace_brcmf_bcdchdr(pktbuf->data); h = (struct brcmf_proto_bcdc_header *)(pktbuf->data); - ifp = brcmf_get_ifp(drvr, BCDC_GET_IF_IDX(h)); - if (IS_ERR_OR_NULL(ifp)) { + tmp_if = brcmf_get_ifp(drvr, BCDC_GET_IF_IDX(h)); + if (!tmp_if) { brcmf_dbg(INFO, "no matching ifp found\n"); return -EBADE; } if (((h->flags & BCDC_FLAG_VER_MASK) >> BCDC_FLAG_VER_SHIFT) != BCDC_PROTO_VER) { brcmf_err("%s: non-BCDC packet received, flags 0x%x\n", - brcmf_ifname(drvr, ifp->ifidx), h->flags); + brcmf_ifname(drvr, tmp_if->ifidx), h->flags); return -EBADE; } if (h->flags & BCDC_FLAG_SUM_GOOD) { brcmf_dbg(BCDC, "%s: BDC rcv, good checksum, flags 0x%x\n", - brcmf_ifname(drvr, ifp->ifidx), h->flags); + brcmf_ifname(drvr, tmp_if->ifidx), h->flags); pktbuf->ip_summed = CHECKSUM_UNNECESSARY; } @@ -312,7 +312,7 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, skb_pull(pktbuf, BCDC_HEADER_LEN); if (do_fws) - brcmf_fws_hdrpull(drvr, ifp->ifidx, h->data_offset << 2, + brcmf_fws_hdrpull(drvr, tmp_if->ifidx, h->data_offset << 2, pktbuf); else skb_pull(pktbuf, h->data_offset << 2); @@ -320,7 +320,7 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, if (pktbuf->len == 0) return -ENODATA; - *ifidx = ifp->ifidx; + *ifp = tmp_if; return 0; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index 1747d59..1f65ce8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -87,7 +87,7 @@ struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx) { if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) { brcmf_err("ifidx %d out of range\n", ifidx); - return ERR_PTR(-ERANGE); + return NULL; } /* The ifidx is the idx to map to matching netdev/ifp. When receiving @@ -539,17 +539,15 @@ void brcmf_rx_frame(struct device *dev, struct sk_buff *skb) struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; struct brcmf_skb_reorder_data *rd; - u8 ifidx; int ret; brcmf_dbg(DATA, "Enter: %s: rxp=%p\n", dev_name(dev), skb); /* process and remove protocol-specific header */ - ret = brcmf_proto_hdrpull(drvr, true, &ifidx, skb); - ifp = drvr->iflist[ifidx]; + ret = brcmf_proto_hdrpull(drvr, true, skb, &ifp); if (ret || !ifp || !ifp->ndev) { - if ((ret != -ENODATA) && ifp) + if (ret != -ENODATA && ifp) ifp->stats.rx_errors++; brcmu_pkt_buf_free_skb(skb); return; @@ -592,17 +590,17 @@ void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success) { struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; - u8 ifidx; + struct brcmf_if *ifp; /* await txstatus signal for firmware if active */ if (brcmf_fws_fc_active(drvr->fws)) { if (!success) brcmf_fws_bustxfail(drvr->fws, txp); } else { - if (brcmf_proto_hdrpull(drvr, false, &ifidx, txp)) + if (brcmf_proto_hdrpull(drvr, false, txp, &ifp)) brcmu_pkt_buf_free_skb(txp); else - brcmf_txfinalize(drvr, txp, ifidx, success); + brcmf_txfinalize(drvr, txp, ifp->ifidx, success); } } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 5017eaa..fd438739 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -1448,7 +1448,7 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, struct sk_buff *skb; struct brcmf_skbuff_cb *skcb; struct brcmf_fws_mac_descriptor *entry = NULL; - u8 ifidx; + struct brcmf_if *ifp; brcmf_dbg(DATA, "flags %d\n", flags); @@ -1497,15 +1497,16 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, } brcmf_fws_macdesc_return_req_credit(skb); - if (brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb)) { + ret = brcmf_proto_hdrpull(fws->drvr, false, skb, &ifp); + if (ret) { brcmu_pkt_buf_free_skb(skb); return -EINVAL; } if (!remove_from_hanger) - ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, ifidx, + ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, ifp->ifidx, genbit, seq); if (remove_from_hanger || ret) - brcmf_txfinalize(fws->drvr, skb, ifidx, true); + brcmf_txfinalize(fws->drvr, skb, ifp->ifidx, true); return 0; } @@ -1848,7 +1849,7 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo, entry->transit_count--; if (entry->suppressed) entry->suppr_transit_count--; - brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb); + (void)brcmf_proto_hdrpull(fws->drvr, false, skb, NULL); goto rollback; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 1271d1d..d582f69 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -522,7 +522,7 @@ static int brcmf_msgbuf_set_dcmd(struct brcmf_pub *drvr, int ifidx, static int brcmf_msgbuf_hdrpull(struct brcmf_pub *drvr, bool do_fws, - u8 *ifidx, struct sk_buff *skb) + struct sk_buff *skb, struct brcmf_if **ifp) { return -ENODEV; } @@ -1082,7 +1082,7 @@ brcmf_msgbuf_rx_skb(struct brcmf_msgbuf *msgbuf, struct sk_buff *skb, struct brcmf_if *ifp; ifp = brcmf_get_ifp(msgbuf->drvr, ifidx); - if (IS_ERR_OR_NULL(ifp) || !ifp->ndev) { + if (!ifp || !ifp->ndev) { brcmf_err("Received pkt for invalid ifidx %d\n", ifidx); brcmu_pkt_buf_free_skb(skb); return; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.h b/drivers/net/wireless/brcm80211/brcmfmac/proto.h index 971172f..d55119d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/proto.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/proto.h @@ -24,8 +24,8 @@ enum proto_addr_mode { struct brcmf_proto { - int (*hdrpull)(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, - struct sk_buff *skb); + int (*hdrpull)(struct brcmf_pub *drvr, bool do_fws, + struct sk_buff *skb, struct brcmf_if **ifp); int (*query_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, uint len); int (*set_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, @@ -46,9 +46,19 @@ int brcmf_proto_attach(struct brcmf_pub *drvr); void brcmf_proto_detach(struct brcmf_pub *drvr); static inline int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, - u8 *ifidx, struct sk_buff *skb) + struct sk_buff *skb, + struct brcmf_if **ifp) { - return drvr->proto->hdrpull(drvr, do_fws, ifidx, skb); + struct brcmf_if *tmp = NULL; + + /* assure protocol is always called with + * non-null initialized pointer. + */ + if (ifp) + *ifp = NULL; + else + ifp = &tmp; + return drvr->proto->hdrpull(drvr, do_fws, skb, ifp); } static inline int brcmf_proto_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, uint len) -- cgit v0.10.2 From ee6e3a3414a48425211f9292b8bfc4524c666c41 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 26 Aug 2015 22:14:55 +0200 Subject: brcmfmac: change parameters for brcmf_remove_interface() Just pass the interface to be removed, ie. the struct brcmf_if instance. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index a293275..0c29bf6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -4983,7 +4983,7 @@ brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg, brcmf_dbg(CONN, "AP mode link down\n"); complete(&cfg->vif_disabled); if (ifp->vif->mbss) - brcmf_remove_interface(ifp->drvr, ifp->bssidx); + brcmf_remove_interface(ifp); return 0; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index 1f65ce8..157f0d7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -887,12 +887,13 @@ static void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx) } } -void brcmf_remove_interface(struct brcmf_pub *drvr, u32 bssidx) +void brcmf_remove_interface(struct brcmf_if *ifp) { - if (drvr->iflist[bssidx]) { - brcmf_fws_del_interface(drvr->iflist[bssidx]); - brcmf_del_if(drvr, bssidx); - } + if (!ifp || WARN_ON(ifp->drvr->iflist[ifp->bssidx] != ifp)) + return; + + brcmf_fws_del_interface(ifp); + brcmf_del_if(ifp->drvr, ifp->bssidx); } int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr) @@ -1122,7 +1123,7 @@ void brcmf_detach(struct device *dev) /* make sure primary interface removed last */ for (i = BRCMF_MAX_IFS-1; i > -1; i--) - brcmf_remove_interface(drvr, i); + brcmf_remove_interface(drvr->iflist[i]); brcmf_cfg80211_detach(drvr->config); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.h b/drivers/net/wireless/brcm80211/brcmfmac/core.h index 1dc5b96..eb0c177 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h @@ -206,7 +206,7 @@ struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx); int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked); struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, char *name, u8 *mac_addr); -void brcmf_remove_interface(struct brcmf_pub *drvr, u32 bssidx); +void brcmf_remove_interface(struct brcmf_if *ifp); int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr); void brcmf_txflowblock_if(struct brcmf_if *ifp, enum brcmf_netif_stop_reason reason, bool state); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c index ec62492..4d4cc2c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c @@ -222,7 +222,7 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, err = brcmf_fweh_call_event_handler(ifp, emsg->event_code, emsg, data); if (ifp && ifevent->action == BRCMF_E_IF_DEL) - brcmf_remove_interface(drvr, ifevent->bssidx); + brcmf_remove_interface(ifp); } /** diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index a9ba775..6a70c06 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -2140,7 +2140,7 @@ static void brcmf_p2p_delete_p2pdev(struct brcmf_p2p_info *p2p, { cfg80211_unregister_wdev(&vif->wdev); p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL; - brcmf_remove_interface(vif->ifp->drvr, vif->ifp->bssidx); + brcmf_remove_interface(vif->ifp); brcmf_free_vif(vif); } -- cgit v0.10.2 From 1c626cf472fce757a69a4c70f038867907b5b808 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 26 Aug 2015 22:14:56 +0200 Subject: brcmfmac: only call brcmf_cfg80211_detach() when attach was successful In brcmf_bus_start() the function brcmf_cfg80211_attach() is called which may fail. If this happens we should not call brcmf_cfg80211_detach() in the failure path as it will result in NULL pointer dereference: brcmf_fweh_activate_events: Set event_msgs error (-5) brcmf_bus_start: failed: -5 brcmf_sdio_firmware_callback: dongle is not responding BUG: unable to handle kernel NULL pointer dereference at 0000000000000068 IP: [] kernfs_find_ns+0x18/0xd0 PGD 0 Oops: 0000 [#1] SMP Modules linked in: brcmfmac(O) brcmutil(O) cfg80211 auth_rpcgss CPU: 1 PID: 45 Comm: kworker/1:1 Tainted: G O Hardware name: Dell Inc. Latitude E6410/07XJP9, BIOS A07 02/15/2011 Workqueue: events request_firmware_work_func task: ffff880036c09ac0 ti: ffff880036dd4000 task.ti: ffff880036dd4000 RIP: 0010:[] [] kernfs_find_ns+0x18/0xd0 RSP: 0018:ffff880036dd7a28 EFLAGS: 00010246 RAX: ffff880036c09ac0 RBX: 0000000000000000 RCX: 000000007fffffff RDX: 0000000000000000 RSI: ffffffff816578b9 RDI: 0000000000000000 RBP: ffff880036dd7a48 R08: 0000000000000000 R09: ffff880036c0b340 R10: 00000000000002ec R11: ffff880036dd7b08 R12: ffffffff816578b9 R13: 0000000000000000 R14: ffffffff816578b9 R15: ffff8800c6c87000 FS: 0000000000000000(0000) GS:ffff88012bc40000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 0000000000000068 CR3: 0000000001a0b000 CR4: 00000000000006e0 Stack: 0000000000000000 ffffffff816578b9 0000000000000000 ffff8800c0d003c8 ffff880036dd7a78 ffffffff811e8ff5 0000000ffffffff1 ffffffff81a9b060 ffff8800c789f880 ffff8800c0d00000 ffff880036dd7a98 ffffffff811ebe0d Call Trace: [] kernfs_find_and_get_ns+0x35/0x60 [] sysfs_unmerge_group+0x1d/0x60 [] dpm_sysfs_remove+0x22/0x60 [] device_del+0x49/0x240 [] rfkill_unregister+0x58/0xc0 [] wiphy_unregister+0xab/0x2f0 [cfg80211] [] brcmf_cfg80211_detach+0x23/0x50 [brcmfmac] [] brcmf_detach+0x86/0xe0 [brcmfmac] [] brcmf_sdio_remove+0x48/0x120 [brcmfmac] [] brcmf_sdiod_remove+0x29/0xd0 [brcmfmac] [] brcmf_ops_sdio_remove+0xb1/0x110 [brcmfmac] [] sdio_bus_remove+0x37/0x100 [mmc_core] [] __device_release_driver+0x96/0x130 [] device_release_driver+0x23/0x30 [] brcmf_sdio_firmware_callback+0x2a8/0x5d0 [brcmfmac] [] brcmf_fw_request_nvram_done+0x15f/0x5e0 [brcmfmac] [] ? devres_add+0x3f/0x50 [] ? usermodehelper_read_unlock+0x15/0x20 [] ? platform_match+0x70/0xa0 [] request_firmware_work_func+0x30/0x60 [] process_one_work+0x14c/0x3d0 [] worker_thread+0x11a/0x450 [] ? process_one_work+0x3d0/0x3d0 [] kthread+0xd2/0xf0 [] ? kthread_create_on_node+0x180/0x180 [] ret_from_fork+0x3f/0x70 [] ? kthread_create_on_node+0x180/0x180 Code: e9 40 fe ff ff 48 89 d8 eb 87 66 0f 1f 84 00 00 00 00 00 66 66 66 66 90 55 48 89 e5 41 56 49 89 f6 41 55 49 89 d5 31 d2 41 54 53 <0f> b7 47 68 48 8b 5f 48 66 c1 e8 05 83 e0 01 4d 85 ed 0f b6 c8 RIP [] kernfs_find_ns+0x18/0xd0 RSP CR2: 0000000000000068 ---[ end trace 87d6ec0d3fe46740 ]--- Reported-by: Daniel (Deognyoun) Kim Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index 157f0d7..126081a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -1049,7 +1049,10 @@ int brcmf_bus_start(struct device *dev) fail: if (ret < 0) { brcmf_err("failed: %d\n", ret); - brcmf_cfg80211_detach(drvr->config); + if (drvr->config) { + brcmf_cfg80211_detach(drvr->config); + drvr->config = NULL; + } if (drvr->fws) { brcmf_fws_del_interface(ifp); brcmf_fws_deinit(drvr); -- cgit v0.10.2 From ddddfed2f9d8422fd5bb5080aa95e478832c0130 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 26 Aug 2015 22:14:57 +0200 Subject: brcmfmac: correct detection of p2pdev interface event The p2pdev interface is setup in firmware resulting in a interface event. This event has role and no-if flag. When role is p2p client and no-if flag is set it indicates that this is the p2pdev interface. This info is used in handling the event and adding interface in the driver. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index 126081a..b9ed0ed 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -795,7 +795,7 @@ fail: } struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, - char *name, u8 *mac_addr) + bool is_p2pdev, char *name, u8 *mac_addr) { struct brcmf_if *ifp; struct net_device *ndev; @@ -821,7 +821,7 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, } } - if (!brcmf_p2p_enable && bssidx == 1) { + if (!brcmf_p2p_enable && is_p2pdev) { /* this is P2P_DEVICE interface */ brcmf_dbg(INFO, "allocate non-netdev interface\n"); ifp = kzalloc(sizeof(*ifp), GFP_KERNEL); @@ -999,12 +999,12 @@ int brcmf_bus_start(struct device *dev) brcmf_dbg(TRACE, "\n"); /* add primary networking interface */ - ifp = brcmf_add_if(drvr, 0, 0, "wlan%d", NULL); + ifp = brcmf_add_if(drvr, 0, 0, false, "wlan%d", NULL); if (IS_ERR(ifp)) return PTR_ERR(ifp); if (brcmf_p2p_enable) - p2p_ifp = brcmf_add_if(drvr, 1, 0, "p2p%d", NULL); + p2p_ifp = brcmf_add_if(drvr, 1, 0, false, "p2p%d", NULL); else p2p_ifp = NULL; if (IS_ERR(p2p_ifp)) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.h b/drivers/net/wireless/brcm80211/brcmfmac/core.h index eb0c177..5fb9f4b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h @@ -205,7 +205,7 @@ char *brcmf_ifname(struct brcmf_pub *drvr, int idx); struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx); int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked); struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, - char *name, u8 *mac_addr); + bool is_p2pdev, char *name, u8 *mac_addr); void brcmf_remove_interface(struct brcmf_if *ifp); int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr); void brcmf_txflowblock_if(struct brcmf_if *ifp, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c index 4d4cc2c..1afaf97 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c @@ -179,6 +179,7 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, { struct brcmf_if_event *ifevent = data; struct brcmf_if *ifp; + bool is_p2pdev; int err = 0; brcmf_dbg(EVENT, "action: %u idx: %u bsscfg: %u flags: %u role: %u\n", @@ -186,18 +187,16 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, ifevent->flags, ifevent->role); /* The P2P Device interface event must not be ignored - * contrary to what firmware tells us. The only way to - * distinguish the P2P Device is by looking at the ifidx - * and bssidx received. + * contrary to what firmware tells us. */ - if (!(ifevent->ifidx == 0 && ifevent->bssidx == 1) && - (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) { + is_p2pdev = (ifevent->flags & BRCMF_E_IF_FLAG_NOIF) && + ifevent->role == BRCMF_E_IF_ROLE_P2P_CLIENT; + if (!is_p2pdev && (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) { brcmf_dbg(EVENT, "event can be ignored\n"); return; } if (ifevent->ifidx >= BRCMF_MAX_IFS) { - brcmf_err("invalid interface index: %u\n", - ifevent->ifidx); + brcmf_err("invalid interface index: %u\n", ifevent->ifidx); return; } @@ -207,7 +206,7 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, brcmf_dbg(EVENT, "adding %s (%pM)\n", emsg->ifname, emsg->addr); ifp = brcmf_add_if(drvr, ifevent->bssidx, ifevent->ifidx, - emsg->ifname, emsg->addr); + is_p2pdev, emsg->ifname, emsg->addr); if (IS_ERR(ifp)) return; brcmf_fws_add_interface(ifp); -- cgit v0.10.2 From 46f3b6ee483898095906023e1ba3af2bddc38af9 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 26 Aug 2015 22:14:58 +0200 Subject: brcmfmac: use brcmf_get_ifp() to map ifidx to struct brcmf_if instance The knowledge on how to map the interface index to a struct brcmf_if instance is in brcmf_get_ifp() so use that function when only the interface index is known instead of accessing brcmf_pub::iflist directly. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c index 0445163..4e33f96 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c @@ -149,7 +149,7 @@ static s32 brcmf_btcoex_params_read(struct brcmf_if *ifp, u32 addr, u32 *data) static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci, bool trump_sco) { - struct brcmf_if *ifp = btci->cfg->pub->iflist[0]; + struct brcmf_if *ifp = brcmf_get_ifp(btci->cfg->pub, 0); if (trump_sco && !btci->saved_regs_part2) { /* this should reduce eSCO agressive @@ -468,7 +468,7 @@ int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif, { struct brcmf_cfg80211_info *cfg = wiphy_priv(vif->wdev.wiphy); struct brcmf_btcoex_info *btci = cfg->btcoex; - struct brcmf_if *ifp = cfg->pub->iflist[0]; + struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0); switch (mode) { case BRCMF_BTCOEX_DISABLED: diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 0c29bf6..cee9ff4 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -6213,7 +6213,7 @@ static void brcmf_free_wiphy(struct wiphy *wiphy) struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, struct device *busdev) { - struct net_device *ndev = drvr->iflist[0]->ndev; + struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev; struct brcmf_cfg80211_info *cfg; struct wiphy *wiphy; struct brcmf_cfg80211_vif *vif; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c index 1e94e94..7c2135f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c @@ -121,7 +121,7 @@ static void brcmf_feat_iovar_int_set(struct brcmf_if *ifp, void brcmf_feat_attach(struct brcmf_pub *drvr) { - struct brcmf_if *ifp = drvr->iflist[0]; + struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan"); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn"); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c index 8d1ab4a..2ca783f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c @@ -221,7 +221,7 @@ static void brcmf_flowring_block(struct brcmf_flowring *flow, u8 flowid, bus_if = dev_get_drvdata(flow->dev); drvr = bus_if->drvr; - ifp = drvr->iflist[ifidx]; + ifp = brcmf_get_ifp(drvr, ifidx); brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FLOW, blocked); spin_unlock_irqrestore(&flow->block_lock, flags); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c index 1afaf97..3330f30 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c @@ -334,7 +334,7 @@ void brcmf_fweh_attach(struct brcmf_pub *drvr) void brcmf_fweh_detach(struct brcmf_pub *drvr) { struct brcmf_fweh_info *fweh = &drvr->fweh; - struct brcmf_if *ifp = drvr->iflist[0]; + struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0); s8 eventmask[BRCMF_EVENTING_MASK_LEN]; if (ifp) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index fd438739..0c80c85 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -972,7 +972,7 @@ static void brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq, u8 if_id) { - struct brcmf_if *ifp = fws->drvr->iflist[!if_id ? 0 : if_id + 1]; + struct brcmf_if *ifp = brcmf_get_ifp(fws->drvr, if_id); if (WARN_ON(!ifp)) return; @@ -2118,6 +2118,7 @@ static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data) int brcmf_fws_init(struct brcmf_pub *drvr) { struct brcmf_fws_info *fws; + struct brcmf_if *ifp; u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS; int rc; u32 mode; @@ -2177,21 +2178,22 @@ int brcmf_fws_init(struct brcmf_pub *drvr) * continue. Set mode back to none indicating not enabled. */ fws->fw_signals = true; - if (brcmf_fil_iovar_int_set(drvr->iflist[0], "tlv", tlv)) { + ifp = brcmf_get_ifp(drvr, 0); + if (brcmf_fil_iovar_int_set(ifp, "tlv", tlv)) { brcmf_err("failed to set bdcv2 tlv signaling\n"); fws->fcmode = BRCMF_FWS_FCMODE_NONE; fws->fw_signals = false; } - if (brcmf_fil_iovar_int_set(drvr->iflist[0], "ampdu_hostreorder", 1)) + if (brcmf_fil_iovar_int_set(ifp, "ampdu_hostreorder", 1)) brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n"); /* Enable seq number reuse, if supported */ - if (brcmf_fil_iovar_int_get(drvr->iflist[0], "wlfc_mode", &mode) == 0) { + if (brcmf_fil_iovar_int_get(ifp, "wlfc_mode", &mode) == 0) { if (BRCMF_FWS_MODE_GET_REUSESEQ(mode)) { mode = 0; BRCMF_FWS_MODE_SET_REUSESEQ(mode, 1); - if (brcmf_fil_iovar_int_set(drvr->iflist[0], + if (brcmf_fil_iovar_int_set(ifp, "wlfc_mode", mode) == 0) { BRCMF_FWS_MODE_SET_REUSESEQ(fws->mode, 1); } -- cgit v0.10.2 From 054e68add65972af20dbf0848e25c80828c5cd60 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 26 Aug 2015 22:14:59 +0200 Subject: brcmfmac: pass struct brcmf_if instance in brcmf_txfinalize() Most call sites of brcmf_txfinalize already have struct brcmf_if instance so pass that to brcmf_txfinalize() as the function needs it anyway. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index b9ed0ed..cd0bffe 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -560,17 +560,11 @@ void brcmf_rx_frame(struct device *dev, struct sk_buff *skb) brcmf_netif_rx(ifp, skb); } -void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx, - bool success) +void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success) { - struct brcmf_if *ifp; struct ethhdr *eh; u16 type; - ifp = drvr->iflist[ifidx]; - if (!ifp) - goto done; - eh = (struct ethhdr *)(txp->data); type = ntohs(eh->h_proto); @@ -582,7 +576,7 @@ void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx, if (!success) ifp->stats.tx_errors++; -done: + brcmu_pkt_buf_free_skb(txp); } @@ -600,7 +594,7 @@ void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success) if (brcmf_proto_hdrpull(drvr, false, txp, &ifp)) brcmu_pkt_buf_free_skb(txp); else - brcmf_txfinalize(drvr, txp, ifp->ifidx, success); + brcmf_txfinalize(ifp, txp, success); } } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.h b/drivers/net/wireless/brcm80211/brcmfmac/core.h index 5fb9f4b..576e7ac 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h @@ -210,8 +210,7 @@ void brcmf_remove_interface(struct brcmf_if *ifp); int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr); void brcmf_txflowblock_if(struct brcmf_if *ifp, enum brcmf_netif_stop_reason reason, bool state); -void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx, - bool success); +void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success); void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb); /* Sets dongle media info (drv_version, mac address). */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 0c80c85..dc4844c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -1506,7 +1506,7 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, ifp->ifidx, genbit, seq); if (remove_from_hanger || ret) - brcmf_txfinalize(fws->drvr, skb, ifp->ifidx, true); + brcmf_txfinalize(ifp, skb, true); return 0; } @@ -1905,7 +1905,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) if (fws->avoid_queueing) { rc = brcmf_proto_txdata(drvr, ifp->ifidx, 0, skb); if (rc < 0) - brcmf_txfinalize(drvr, skb, ifp->ifidx, false); + brcmf_txfinalize(ifp, skb, false); return rc; } @@ -1929,7 +1929,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) brcmf_fws_schedule_deq(fws); } else { brcmf_err("drop skb: no hanger slot\n"); - brcmf_txfinalize(drvr, skb, ifp->ifidx, false); + brcmf_txfinalize(ifp, skb, false); rc = -ENOMEM; } brcmf_fws_unlock(fws); @@ -2009,8 +2009,9 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) ret = brcmf_proto_txdata(drvr, ifidx, 0, skb); brcmf_fws_lock(fws); if (ret < 0) - brcmf_txfinalize(drvr, skb, ifidx, - false); + brcmf_txfinalize(brcmf_get_ifp(drvr, + ifidx), + skb, false); if (fws->bus_flow_blocked) break; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index d582f69..7eff9de 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -873,7 +873,11 @@ brcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf) commonring = msgbuf->flowrings[flowid]; atomic_dec(&commonring->outstanding_tx); - brcmf_txfinalize(msgbuf->drvr, skb, tx_status->msg.ifidx, true); + /* Hante: i believe this was a bug as tx_status->msg.ifidx was used + * in brcmf_txfinalize as index in drvr->iflist. Can you confirm/deny? + */ + brcmf_txfinalize(brcmf_get_ifp(msgbuf->drvr, tx_status->msg.ifidx), + skb, true); } -- cgit v0.10.2 From 291da87b69109924e6ee526606a783668d2232e0 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 26 Aug 2015 22:15:00 +0200 Subject: brcmfmac: add mapping for interface index to bsscfg index Because the P2P Device interface in firmware uses the same interface index as the primary interface we use the bsscfg index as index in the struct brcmf_pub::iflist. However, in the data path we get the interface index and not the bsscfg index. So we need a mapping of interface index to bsscfg index, which can be determined upon handle adding the interface. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index cd0bffe..5d81b74 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -85,21 +85,20 @@ char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx) struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx) { + struct brcmf_if *ifp; + s32 bssidx; + if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) { brcmf_err("ifidx %d out of range\n", ifidx); return NULL; } - /* The ifidx is the idx to map to matching netdev/ifp. When receiving - * events this is easy because it contains the bssidx which maps - * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd. - * bssidx 1 is used for p2p0 and no data can be received or - * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0 - */ - if (ifidx) - ifidx++; + ifp = NULL; + bssidx = drvr->if2bss[ifidx]; + if (bssidx >= 0) + ifp = drvr->iflist[bssidx]; - return drvr->iflist[ifidx]; + return ifp; } static void _brcmf_set_multicast_list(struct work_struct *work) @@ -831,6 +830,8 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, ifp = netdev_priv(ndev); ifp->ndev = ndev; + /* store mapping ifidx to bssidx */ + drvr->if2bss[ifidx] = bssidx; } ifp->drvr = drvr; @@ -855,6 +856,7 @@ static void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx) struct brcmf_if *ifp; ifp = drvr->iflist[bssidx]; + drvr->if2bss[ifp->ifidx] = -1; drvr->iflist[bssidx] = NULL; if (!ifp) { brcmf_err("Null interface, idx=%d\n", bssidx); @@ -862,6 +864,7 @@ static void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx) } brcmf_dbg(TRACE, "Enter, idx=%d, ifidx=%d\n", bssidx, ifp->ifidx); if (ifp->ndev) { + drvr->if2bss[ifp->ifidx] = -1; if (bssidx == 0) { if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) { rtnl_lock(); @@ -926,6 +929,7 @@ int brcmf_attach(struct device *dev) if (!drvr) return -ENOMEM; + memset(drvr->if2bss, 0xFF, sizeof(drvr->if2bss)); mutex_init(&drvr->proto_block); /* Link to bus module */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.h b/drivers/net/wireless/brcm80211/brcmfmac/core.h index 576e7ac..d81ff95 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h @@ -122,6 +122,7 @@ struct brcmf_pub { struct mac_address addresses[BRCMF_MAX_IFS]; struct brcmf_if *iflist[BRCMF_MAX_IFS]; + s32 if2bss[BRCMF_MAX_IFS]; struct mutex proto_block; unsigned char proto_buf[BRCMF_DCMD_MAXLEN]; -- cgit v0.10.2 From 9d6c1dc4f9136227b39faf98ae46d0228fe1fa19 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 26 Aug 2015 22:15:01 +0200 Subject: brcmfmac: add dedicated debug level for firmware console logging Both PCIe and SDIO devices have the possibility to log the firmware console output in kernel log. For PCIe it is logged when PCIE debug level is enabled. For SDIO it is logged when user specifies a non-zero console interval through debugfs. This patch tries to make it a bit more consistent. The firmware console output is only logged when FWCON debug level is enabled. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Pontus Fuchs Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/debug.h b/drivers/net/wireless/brcm80211/brcmfmac/debug.h index eb0b8c4..48648ca 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/debug.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/debug.h @@ -37,6 +37,7 @@ #define BRCMF_SDIO_VAL 0x00020000 #define BRCMF_MSGBUF_VAL 0x00040000 #define BRCMF_PCIE_VAL 0x00080000 +#define BRCMF_FWCON_VAL 0x00100000 /* set default print format */ #undef pr_fmt @@ -78,6 +79,7 @@ do { \ #define BRCMF_GLOM_ON() (brcmf_msg_level & BRCMF_GLOM_VAL) #define BRCMF_EVENT_ON() (brcmf_msg_level & BRCMF_EVENT_VAL) #define BRCMF_FIL_ON() (brcmf_msg_level & BRCMF_FIL_VAL) +#define BRCMF_FWCON_ON() (brcmf_msg_level & BRCMF_FWCON_VAL) #else /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */ @@ -90,6 +92,7 @@ do { \ #define BRCMF_GLOM_ON() 0 #define BRCMF_EVENT_ON() 0 #define BRCMF_FIL_ON() 0 +#define BRCMF_FWCON_ON() 0 #endif /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index 3a98c43..e75d04e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -644,7 +644,7 @@ static void brcmf_pcie_bus_console_init(struct brcmf_pciedev_info *devinfo) addr = console->base_addr + BRCMF_CONSOLE_BUFSIZE_OFFSET; console->bufsize = brcmf_pcie_read_tcm32(devinfo, addr); - brcmf_dbg(PCIE, "Console: base %x, buf %x, size %d\n", + brcmf_dbg(FWCON, "Console: base %x, buf %x, size %d\n", console->base_addr, console->buf_addr, console->bufsize); } @@ -656,6 +656,9 @@ static void brcmf_pcie_bus_console_read(struct brcmf_pciedev_info *devinfo) u8 ch; u32 newidx; + if (!BRCMF_FWCON_ON()) + return; + console = &devinfo->shared.console; addr = console->base_addr + BRCMF_CONSOLE_WRITEIDX_OFFSET; newidx = brcmf_pcie_read_tcm32(devinfo, addr); @@ -677,7 +680,7 @@ static void brcmf_pcie_bus_console_read(struct brcmf_pciedev_info *devinfo) } if (ch == '\n') { console->log_str[console->log_idx] = 0; - brcmf_dbg(PCIE, "CONSOLE: %s", console->log_str); + pr_debug("CONSOLE: %s", console->log_str); console->log_idx = 0; } } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index f990e3d..e41bf44 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -123,6 +123,7 @@ struct rte_console { #define BRCMF_FIRSTREAD (1 << 6) +#define BRCMF_CONSOLE 10 /* watchdog interval to poll console */ /* SBSDIO_DEVICE_CTL */ @@ -3204,6 +3205,8 @@ static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) if (IS_ERR_OR_NULL(dentry)) return; + bus->console_interval = BRCMF_CONSOLE; + brcmf_debugfs_add_entry(drvr, "forensics", brcmf_sdio_forensic_read); brcmf_debugfs_add_entry(drvr, "counters", brcmf_debugfs_sdio_count_read); @@ -3613,7 +3616,7 @@ static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) } #ifdef DEBUG /* Poll for console output periodically */ - if (bus->sdiodev->state == BRCMF_SDIOD_DATA && + if (bus->sdiodev->state == BRCMF_SDIOD_DATA && BRCMF_FWCON_ON() && bus->console_interval != 0) { bus->console.count += BRCMF_WD_POLL_MS; if (bus->console.count >= bus->console_interval) { -- cgit v0.10.2 From 03cca20a6ee15b25487b4992eda2c3bf08d1f1c1 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 26 Aug 2015 22:15:02 +0200 Subject: brcmfmac: remove ifidx parameter from brcmf_fws_txstatus_suppressed() The brcmf_fws_txstatus_suppressed() function prototype specifies an ifidx parameter which is not used within the function implementation. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index dc4844c..ed12486 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -1398,7 +1398,7 @@ done: } static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, - struct sk_buff *skb, u8 ifidx, + struct sk_buff *skb, u32 genbit, u16 seq) { struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; @@ -1503,7 +1503,7 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, return -EINVAL; } if (!remove_from_hanger) - ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, ifp->ifidx, + ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, genbit, seq); if (remove_from_hanger || ret) brcmf_txfinalize(ifp, skb, true); -- cgit v0.10.2 From c67d41ba6ff3a4be9aecb9df202e05e149832df5 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 26 Aug 2015 22:15:03 +0200 Subject: brcmfmac: change prototype for brcmf_fws_hdrpull() Instead of passing ifidx and drvr just pass struct brcmf_if pointer which holds both parameters. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c index aa5ed65..288c84e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c @@ -312,8 +312,7 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, skb_pull(pktbuf, BCDC_HEADER_LEN); if (do_fws) - brcmf_fws_hdrpull(drvr, tmp_if->ifidx, h->data_offset << 2, - pktbuf); + brcmf_fws_hdrpull(tmp_if, h->data_offset << 2, pktbuf); else skb_pull(pktbuf, h->data_offset << 2); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index ed12486..086cac3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -1616,11 +1616,10 @@ static int brcmf_fws_notify_bcmc_credit_support(struct brcmf_if *ifp, return 0; } -int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, - struct sk_buff *skb) +void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb) { struct brcmf_skb_reorder_data *rd; - struct brcmf_fws_info *fws = drvr->fws; + struct brcmf_fws_info *fws = ifp->drvr->fws; u8 *signal_data; s16 data_len; u8 type; @@ -1630,20 +1629,20 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, s32 err; brcmf_dbg(HDRS, "enter: ifidx %d, skblen %u, sig %d\n", - ifidx, skb->len, signal_len); + ifp->ifidx, skb->len, siglen); - WARN_ON(signal_len > skb->len); + WARN_ON(siglen > skb->len); - if (!signal_len) - return 0; + if (!siglen) + return; /* if flow control disabled, skip to packet data and leave */ if ((!fws) || (!fws->fw_signals)) { - skb_pull(skb, signal_len); - return 0; + skb_pull(skb, siglen); + return; } fws->stats.header_pulls++; - data_len = signal_len; + data_len = siglen; signal_data = skb->data; status = BRCMF_FWS_RET_OK_NOSCHEDULE; @@ -1731,14 +1730,12 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, /* signalling processing result does * not affect the actual ethernet packet. */ - skb_pull(skb, signal_len); + skb_pull(skb, siglen); /* this may be a signal-only packet */ if (skb->len == 0) fws->stats.header_only_pkt++; - - return 0; } static u8 brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h index 9fc8609..a36bac1 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h @@ -21,8 +21,7 @@ int brcmf_fws_init(struct brcmf_pub *drvr); void brcmf_fws_deinit(struct brcmf_pub *drvr); bool brcmf_fws_fc_active(struct brcmf_fws_info *fws); -int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, - struct sk_buff *skb); +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); void brcmf_fws_reset_interface(struct brcmf_if *ifp); -- cgit v0.10.2 From 95ef12394a4d002c7ed3bcbed224f3391792a02b Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 26 Aug 2015 22:15:04 +0200 Subject: brcmfmac: introduce brcmf_net_detach() function In case of error during brcmf_bus_start() the network interfaces were freed using free_netdev(). However, the interfaces may have additional memory allocated which is not freed. The netdev has destructor set to brcmf_cfg80211_free_netdev() which frees the additional memory if allocated and call free_netdev(). The brcmf_net_detach() either calls brcmf_cfg80211_free_netdev() directly or uses unregister_netdev() when struct net_device::reg_state indicates the netdev was registered. Reported-by: Daniel (Deognyoun) Kim Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index cee9ff4..600098d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -4747,7 +4747,8 @@ void brcmf_cfg80211_free_netdev(struct net_device *ndev) ifp = netdev_priv(ndev); vif = ifp->vif; - brcmf_free_vif(vif); + if (vif) + brcmf_free_vif(vif); free_netdev(ndev); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index 5d81b74..17658b5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -718,8 +718,6 @@ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked) } brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name); - - ndev->destructor = brcmf_cfg80211_free_netdev; return 0; fail: @@ -729,6 +727,14 @@ fail: return -EBADE; } +static void brcmf_net_detach(struct net_device *ndev) +{ + if (ndev->reg_state == NETREG_REGISTERED) + unregister_netdev(ndev); + else + brcmf_cfg80211_free_netdev(ndev); +} + static int brcmf_net_p2p_open(struct net_device *ndev) { brcmf_dbg(TRACE, "Enter\n"); @@ -805,8 +811,7 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, ifp->ndev->name); if (ifidx) { netif_stop_queue(ifp->ndev); - unregister_netdev(ifp->ndev); - free_netdev(ifp->ndev); + brcmf_net_detach(ifp->ndev); drvr->iflist[bssidx] = NULL; } else { brcmf_err("ignore IF event\n"); @@ -828,6 +833,7 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, if (!ndev) return ERR_PTR(-ENOMEM); + ndev->destructor = brcmf_cfg80211_free_netdev; ifp = netdev_priv(ndev); ifp->ndev = ndev; /* store mapping ifidx to bssidx */ @@ -879,8 +885,7 @@ static void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx) cancel_work_sync(&ifp->setmacaddr_work); cancel_work_sync(&ifp->multicast_work); } - /* unregister will take care of freeing it */ - unregister_netdev(ifp->ndev); + brcmf_net_detach(ifp->ndev); } } @@ -1056,11 +1061,11 @@ fail: brcmf_fws_deinit(drvr); } if (drvr->iflist[0]) { - free_netdev(ifp->ndev); + brcmf_net_detach(ifp->ndev); drvr->iflist[0] = NULL; } if (p2p_ifp) { - free_netdev(p2p_ifp->ndev); + brcmf_net_detach(p2p_ifp->ndev); drvr->iflist[1] = NULL; } return ret; -- cgit v0.10.2 From 07fe2e38c7fd19dc6a5468d0661fc75586ce9ca4 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 27 Aug 2015 16:14:06 +0200 Subject: brcmfmac: Reset PCIE devices after recognition. When PCIE type devices are being FW reloaded without being properly reset then the device ends up in a locked state, requiring the device to be completely powered down. This patch adds a reset through watchdog at the moment the device (cores) has been recognized. This will solve warm reboot issues. Cc: Rafal Milecki Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index 288f831..e3b8e23 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -101,6 +101,9 @@ /* ARM Cortex M3 core, ID 0x82a */ #define BCM4329_CORE_ARM_BASE 0x18002000 +/* Max possibly supported memory size (limited by IO mapped memory) */ +#define BRCMF_CHIP_MAX_MEMSIZE (4 * 1024 * 1024) + #define CORE_SB(base, field) \ (base + SBCONFIGOFF + offsetof(struct sbconfig, field)) #define SBCOREREV(sbidh) \ @@ -687,6 +690,12 @@ static int brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) brcmf_err("RAM size is undetermined\n"); return -ENOMEM; } + + if (ci->pub.ramsize > BRCMF_CHIP_MAX_MEMSIZE) { + brcmf_err("RAM size is incorrect\n"); + return -ENOMEM; + } + return 0; } @@ -899,6 +908,15 @@ static int brcmf_chip_recognition(struct brcmf_chip_priv *ci) /* assure chip is passive for core access */ brcmf_chip_set_passive(&ci->pub); + + /* Call bus specific reset function now. Cores have been determined + * but further access may require a chip specific reset at this point. + */ + if (ci->ops->reset) { + ci->ops->reset(ci->ctx, &ci->pub); + brcmf_chip_set_passive(&ci->pub); + } + return brcmf_chip_get_raminfo(ci); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.h b/drivers/net/wireless/brcm80211/brcmfmac/chip.h index 60dcb38..f6b5fee 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.h @@ -73,6 +73,7 @@ struct brcmf_buscore_ops { u32 (*read32)(void *ctx, u32 addr); void (*write32)(void *ctx, u32 addr, u32 value); int (*prepare)(void *ctx); + int (*reset)(void *ctx, struct brcmf_chip *chip); int (*setup)(void *ctx, struct brcmf_chip *chip); void (*activate)(void *ctx, struct brcmf_chip *chip, u32 rstvec); }; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index e75d04e..255d49a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -74,6 +74,8 @@ enum brcmf_pcie_state { #define BRCMF_PCIE_REG_INTMASK 0x94 #define BRCMF_PCIE_REG_SBMBX 0x98 +#define BRCMF_PCIE_REG_LINK_STATUS_CTRL 0xBC + #define BRCMF_PCIE_PCIE2REG_INTMASK 0x24 #define BRCMF_PCIE_PCIE2REG_MAILBOXINT 0x48 #define BRCMF_PCIE_PCIE2REG_MAILBOXMASK 0x4C @@ -466,6 +468,7 @@ brcmf_pcie_select_core(struct brcmf_pciedev_info *devinfo, u16 coreid) static void brcmf_pcie_reset_device(struct brcmf_pciedev_info *devinfo) { + struct brcmf_core *core; u16 cfg_offset[] = { BRCMF_PCIE_CFGREG_STATUS_CMD, BRCMF_PCIE_CFGREG_PM_CSR, BRCMF_PCIE_CFGREG_MSI_CAP, @@ -484,32 +487,38 @@ static void brcmf_pcie_reset_device(struct brcmf_pciedev_info *devinfo) if (!devinfo->ci) return; + /* Disable ASPM */ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, - BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL); - lsc = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA); + pci_read_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL, + &lsc); val = lsc & (~BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB); - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, val); + pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL, + val); + /* Watchdog reset */ brcmf_pcie_select_core(devinfo, BCMA_CORE_CHIPCOMMON); WRITECC32(devinfo, watchdog, 4); msleep(100); + /* Restore ASPM */ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, - BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL); - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, lsc); - - brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); - for (i = 0; i < ARRAY_SIZE(cfg_offset); i++) { - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, - cfg_offset[i]); - val = brcmf_pcie_read_reg32(devinfo, - BRCMF_PCIE_PCIE2REG_CONFIGDATA); - brcmf_dbg(PCIE, "config offset 0x%04x, value 0x%04x\n", - cfg_offset[i], val); - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, - val); + pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL, + lsc); + + core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_PCIE2); + if (core->rev <= 13) { + for (i = 0; i < ARRAY_SIZE(cfg_offset); i++) { + brcmf_pcie_write_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_CONFIGADDR, + cfg_offset[i]); + val = brcmf_pcie_read_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_CONFIGDATA); + brcmf_dbg(PCIE, "config offset 0x%04x, value 0x%04x\n", + cfg_offset[i], val); + brcmf_pcie_write_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_CONFIGDATA, + val); + } } } @@ -519,8 +528,6 @@ static void brcmf_pcie_attach(struct brcmf_pciedev_info *devinfo) u32 config; brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); - if (brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_INTMASK) != 0) - brcmf_pcie_reset_device(devinfo); /* BAR1 window may not be sized properly */ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, 0x4e0); @@ -1636,6 +1643,23 @@ static int brcmf_pcie_buscoreprep(void *ctx) } +static int brcmf_pcie_buscore_reset(void *ctx, struct brcmf_chip *chip) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; + u32 val; + + devinfo->ci = chip; + brcmf_pcie_reset_device(devinfo); + + val = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT); + if (val != 0xffffffff) + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, + val); + + return 0; +} + + static void brcmf_pcie_buscore_activate(void *ctx, struct brcmf_chip *chip, u32 rstvec) { @@ -1647,6 +1671,7 @@ static void brcmf_pcie_buscore_activate(void *ctx, struct brcmf_chip *chip, static const struct brcmf_buscore_ops brcmf_pcie_buscore_ops = { .prepare = brcmf_pcie_buscoreprep, + .reset = brcmf_pcie_buscore_reset, .activate = brcmf_pcie_buscore_activate, .read32 = brcmf_pcie_buscore_read32, .write32 = brcmf_pcie_buscore_write32, @@ -1814,7 +1839,6 @@ brcmf_pcie_remove(struct pci_dev *pdev) brcmf_pcie_intr_disable(devinfo); brcmf_detach(&pdev->dev); - brcmf_pcie_reset_device(devinfo); kfree(bus->bus_priv.pcie); kfree(bus->msgbuf->flowrings); -- cgit v0.10.2 From 36925e52c5ac9d10a553d1339e13a61bfbdd4ba4 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 31 Aug 2015 10:46:45 -0700 Subject: mwifiex: Make mwifiex_dbg a function, reduce object size The mwifiex_dbg macro has two tests that could be consolidated into a function reducing overall object size ~10KB (~4%). So convert the macro into a function. $ size drivers/net/wireless/mwifiex/built-in.o* (x86-64 defconfig) text data bss dec hex filename 233102 8628 4809 246539 3c30b drivers/net/wireless/mwifiex/built-in.o.new 243949 8628 4809 257386 3ed6a drivers/net/wireless/mwifiex/built-in.o.old Signed-off-by: Joe Perches Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 278dc94..4fa8ca3 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -1447,6 +1447,26 @@ exit_sem_err: } EXPORT_SYMBOL_GPL(mwifiex_remove_card); +void _mwifiex_dbg(const struct mwifiex_adapter *adapter, int mask, + const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + if (!adapter->dev || !(adapter->debug_mask & mask)) + return; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + dev_info(adapter->dev, "%pV", &vaf); + + va_end(args); +} +EXPORT_SYMBOL_GPL(_mwifiex_dbg); + /* * This function initializes the module. * diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 6b95121..96663214 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -48,6 +48,9 @@ extern const char driver_version[]; +struct mwifiex_adapter; +struct mwifiex_private; + enum { MWIFIEX_ASYNC_CMD, MWIFIEX_SYNC_CMD @@ -180,12 +183,11 @@ enum MWIFIEX_DEBUG_LEVEL { MWIFIEX_DBG_FATAL | \ MWIFIEX_DBG_ERROR) -#define mwifiex_dbg(adapter, dbg_mask, fmt, args...) \ -do { \ - if ((adapter)->debug_mask & MWIFIEX_DBG_##dbg_mask) \ - if ((adapter)->dev) \ - dev_info((adapter)->dev, fmt, ## args); \ -} while (0) +__printf(3, 4) +void _mwifiex_dbg(const struct mwifiex_adapter *adapter, int mask, + const char *fmt, ...); +#define mwifiex_dbg(adapter, mask, fmt, ...) \ + _mwifiex_dbg(adapter, MWIFIEX_DBG_##mask, fmt, ##__VA_ARGS__) #define DEBUG_DUMP_DATA_MAX_LEN 128 #define mwifiex_dbg_dump(adapter, dbg_mask, str, buf, len) \ @@ -506,9 +508,6 @@ enum mwifiex_iface_work_flags { MWIFIEX_IFACE_WORK_CARD_RESET, }; -struct mwifiex_adapter; -struct mwifiex_private; - struct mwifiex_private { struct mwifiex_adapter *adapter; u8 bss_type; -- cgit v0.10.2 From 61d36370e24bb5d8f83481aa6f76fc05385c75c9 Mon Sep 17 00:00:00 2001 From: Miaoqing Pan Date: Tue, 1 Sep 2015 10:56:09 +0800 Subject: ath9k: enable hw manual peak calibration for QCA9561 This patch fix https://lists.openwrt.org/pipermail/openwrt-devel/ 2015-August/034979.html. As the peak detect calibration is set incorrectly. Signed-off-by: Miaoqing Pan Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 174442b..0c39199 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -1249,7 +1249,8 @@ static void ar9003_hw_manual_peak_cal(struct ath_hw *ah, u8 chain, bool is_2g) REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR, 0x0); - if (AR_SREV_9003_PCOEM(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah)) { + if (AR_SREV_9003_PCOEM(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah) || + AR_SREV_9561(ah)) { if (is_2g) REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), AR_PHY_65NM_RXRF_AGC_AGC2G_DBDAC_OVR, @@ -1640,7 +1641,8 @@ static bool ar9003_hw_init_cal_soc(struct ath_hw *ah, skip_tx_iqcal: if (run_agc_cal || !(ah->ah_flags & AH_FASTCC)) { - if (AR_SREV_9330_11(ah) || AR_SREV_9531(ah) || AR_SREV_9550(ah)) { + if (AR_SREV_9330_11(ah) || AR_SREV_9531(ah) || AR_SREV_9550(ah) || + AR_SREV_9561(ah)) { for (i = 0; i < AR9300_MAX_CHAINS; i++) { if (!(ah->rxchainmask & (1 << i))) continue; -- cgit v0.10.2 From 76ea6fdbeb2540ea8a06189b519175ee5616ab56 Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Wed, 2 Sep 2015 16:56:42 -0700 Subject: net/wireless/wl18xx: Add missing MODULE_FIRMWARE Fixes the output of 'modinfo --field firmware'. Signed-off-by: Geoff Levand Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index abbf054..50cce42 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -2115,3 +2115,4 @@ MODULE_PARM_DESC(num_rx_desc_param, MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Luciano Coelho "); MODULE_FIRMWARE(WL18XX_FW_NAME); +MODULE_FIRMWARE(WL18XX_CONF_FILE_NAME); -- cgit v0.10.2 From c452d944bee09c8c2529d965f10999d80991402a Mon Sep 17 00:00:00 2001 From: Hiroaki KAWAI Date: Thu, 10 Sep 2015 18:04:45 +0000 Subject: carl9170: fix bad rssi reading Fix rssi calculation error which was introduced in otus to ar9170 porting. Signed-off-by: Hiroaki KAWAI Acked-by: Christian Lamparter Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c index 924135b..d66533c 100644 --- a/drivers/net/wireless/ath/carl9170/rx.c +++ b/drivers/net/wireless/ath/carl9170/rx.c @@ -453,7 +453,7 @@ static void carl9170_rx_phy_status(struct ar9170 *ar, /* post-process RSSI */ for (i = 0; i < 7; i++) if (phy->rssi[i] & 0x80) - phy->rssi[i] = ((phy->rssi[i] & 0x7f) + 1) & 0x7f; + phy->rssi[i] = ((~phy->rssi[i] & 0x7f) + 1) & 0x7f; /* TODO: we could do something with phy_errors */ status->signal = ar->noise[0] + phy->rssi_combined; -- cgit v0.10.2 From 862a336c8302695cbac6d8d752ee9a2429487478 Mon Sep 17 00:00:00 2001 From: Jan Kaisrlik Date: Thu, 17 Sep 2015 14:03:46 +0200 Subject: ath9k: Add support for OCB mode The patch adds support for "outside the context of a BSS"(OCB) mode to ath9k driver and extends debugfs files by OCB ralated information. This patch was tested on AR9380-AL1A cards. Signed-off-by: Jan Kaisrlik Cc: Michal Sojka Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index c85c479..b42f4a9 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -635,6 +635,7 @@ struct ath9k_vif_iter_data { int nstations; /* number of station vifs */ int nwds; /* number of WDS vifs */ int nadhocs; /* number of adhoc vifs */ + int nocbs; /* number of OCB vifs */ struct ieee80211_vif *primary_sta; }; diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index da32c8f..6de64cf 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -741,8 +741,8 @@ static int read_file_misc(struct seq_file *file, void *data) i++, (int)(ctx->assigned), iter_data.naps, iter_data.nstations, iter_data.nmeshes, iter_data.nwds); - seq_printf(file, " ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n", - iter_data.nadhocs, sc->cur_chan->nvifs, + seq_printf(file, " ADHOC: %i OCB: %i TOTAL: %hi BEACON-VIF: %hi\n", + iter_data.nadhocs, iter_data.nocbs, sc->cur_chan->nvifs, sc->nbcnvifs); } diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index efe77db..8647ab7 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -740,7 +740,8 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_MESH_POINT); + BIT(NL80211_IFTYPE_MESH_POINT) | + BIT(NL80211_IFTYPE_OCB); hw->wiphy->iface_combinations = &if_comb; hw->wiphy->n_iface_combinations = 1; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 1dd0339..bdfff46 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1241,6 +1241,7 @@ static void ath9k_hw_set_operating_mode(struct ath_hw *ah, int opmode) break; } /* fall through */ + case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_AP: set |= AR_STA_ID1_STA_AP; diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 57f95f2..5d532c7 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -855,7 +855,8 @@ 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) | - BIT(NL80211_IFTYPE_WDS); + BIT(NL80211_IFTYPE_WDS) | + BIT(NL80211_IFTYPE_OCB); if (ath9k_is_chanctx_enabled()) hw->wiphy->interface_modes |= diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index c27143b..56abb9d 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -938,6 +938,9 @@ static void ath9k_vif_iter(struct ath9k_vif_iter_data *iter_data, if (avp->assoc && !iter_data->primary_sta) iter_data->primary_sta = vif; break; + case NL80211_IFTYPE_OCB: + iter_data->nocbs++; + break; case NL80211_IFTYPE_ADHOC: iter_data->nadhocs++; if (vif->bss_conf.enable_beacon) @@ -1111,6 +1114,8 @@ void ath9k_calculate_summary_state(struct ath_softc *sc, if (iter_data.nmeshes) ah->opmode = NL80211_IFTYPE_MESH_POINT; + else if (iter_data.nocbs) + ah->opmode = NL80211_IFTYPE_OCB; else if (iter_data.nwds) ah->opmode = NL80211_IFTYPE_AP; else if (iter_data.nadhocs) @@ -1760,7 +1765,8 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, ath9k_calculate_summary_state(sc, avp->chanctx); } - if (changed & BSS_CHANGED_IBSS) { + if ((changed & BSS_CHANGED_IBSS) || + (changed & BSS_CHANGED_OCB)) { memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); common->curaid = bss_conf->aid; ath9k_hw_write_associd(sc->sc_ah); diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index d3189da..994daf6 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -403,7 +403,7 @@ u32 ath_calcrxfilter(struct ath_softc *sc) (sc->cur_chan->nvifs <= 1) && !(sc->cur_chan->rxfilter & FIF_BCN_PRBRESP_PROMISC)) rfilt |= ATH9K_RX_FILTER_MYBEACON; - else + else if (sc->sc_ah->opmode != NL80211_IFTYPE_OCB) rfilt |= ATH9K_RX_FILTER_BEACON; if ((sc->sc_ah->opmode == NL80211_IFTYPE_AP) || -- cgit v0.10.2 From 32677b25849f627aba27235f4cbf65663a0cc1a6 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Fri, 18 Sep 2015 20:37:27 +0800 Subject: wlcore: align reg_ch_conf_last[] to 64bit Align fields reg_ch_conf_last and reg_ch_conf_pending of struct wl1271{} to 64bit. Without this, on 64bit ARM, wlcore_set_pending_regdomain_ch() fails at the point it calls set_bit(ch_bit_idx, (long*)wl->reg_ch_conf_pending); Here is the error message while doing iw wlan0 scan or connect: [ 10.666857] wlcore: IRQ work [ 10.670046] wlcore: intr: 0x40 (fw_rx_counter = 1, drv_rx_counter = 0, tx_results_counter = 0) [ 10.678697] wlcore: WL1271_ACX_INTR_DATA [ 10.682810] Unhandled fault: alignment fault (0x96000021) at 0xffffffc037a817f4 [ 10.690139] Internal error: : 96000021 [#1] PREEMPT SMP [ 10.695366] Modules linked in: [ 10.698437] CPU: 3 PID: 894 Comm: irq/60-wl18xx Tainted: G W 4.2.0-rc6-linaro-hikey #2 [ 10.707501] Hardware name: HiKey Development Board (DT) [ 10.712733] task: ffffffc03a9d1680 ti: ffffffc039e18000 task.ti: ffffffc039e18000 [ 10.720239] PC is at set_bit+0x14/0x30 [ 10.724002] LR is at wlcore_set_pending_regdomain_ch+0x40/0x4c Signed-off-by: Guodong Xu Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index a1b6040..906be6a 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -318,7 +318,7 @@ struct wl1271 { bool watchdog_recovery; /* Reg domain last configuration */ - u32 reg_ch_conf_last[2]; + u32 reg_ch_conf_last[2] __aligned(8); /* Reg domain pending configuration */ u32 reg_ch_conf_pending[2]; -- cgit v0.10.2 From 65405c51bb2e9d2a348f5402443e31f82411aabb Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Fri, 18 Sep 2015 06:32:03 -0700 Subject: mwifiex: fix typo in del_virtual_intf() function Interface counters should be incremented in add_virtual_intf() and decremented in del_virtual_intf() function. This patch corrects it. Signed-off-by: Xinming Hu Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index ff63cb5..d814340 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -2859,14 +2859,14 @@ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: - adapter->curr_iface_comb.sta_intf++; + adapter->curr_iface_comb.sta_intf--; break; case NL80211_IFTYPE_AP: - adapter->curr_iface_comb.uap_intf++; + adapter->curr_iface_comb.uap_intf--; break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: - adapter->curr_iface_comb.p2p_intf++; + adapter->curr_iface_comb.p2p_intf--; break; default: mwifiex_dbg(adapter, ERROR, -- cgit v0.10.2 From 0a252abb6ad75260353a28c08b904c2c9d258d2c Mon Sep 17 00:00:00 2001 From: Zhaoyang Liu Date: Fri, 18 Sep 2015 06:32:04 -0700 Subject: mwifiex: update amsdu tx packet time stamp This patch fixes the issue of delay time in A-MSDU tx packet. In generated new A-MSDU packet, the time stamp is initialized to zero. Choose the time of first MSDU packet as aggregated packet's time. Signed-off-by: Zhaoyang Liu Signed-off-by: Cathy Luo Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c index f7c7172..6cb0d33 100644 --- a/drivers/net/wireless/mwifiex/11n_aggr.c +++ b/drivers/net/wireless/mwifiex/11n_aggr.c @@ -202,6 +202,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_AGGR_PKT; skb_aggr->priority = skb_src->priority; + skb_aggr->tstamp = skb_src->tstamp; do_gettimeofday(&tv); skb_aggr->tstamp = timeval_to_ktime(tv); -- cgit v0.10.2 From e3ad3d5b6ab2a9876773fe99d9d1866d3c1744ae Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Fri, 18 Sep 2015 06:32:05 -0700 Subject: mwifiex: minor corrections in multiport aggregation code This patch includes below changes 1) Check if multiport aggregation feature flag while doing rx length validation check. 2) Reset buffer size variables when buffers are freed. Signed-off-by: Xinming Hu Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 5d05c6f..d35210c 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -1606,8 +1606,9 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) (rx_len + MWIFIEX_SDIO_BLOCK_SIZE - 1) / MWIFIEX_SDIO_BLOCK_SIZE; if (rx_len <= INTF_HEADER_LEN || - (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) > - card->mpa_rx.buf_size) { + (card->mpa_rx.enabled && + ((rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) > + card->mpa_rx.buf_size))) { mwifiex_dbg(adapter, ERROR, "invalid rx_len=%d\n", rx_len); @@ -1925,6 +1926,8 @@ error: if (ret) { kfree(card->mpa_tx.buf); kfree(card->mpa_rx.buf); + card->mpa_tx.buf_size = 0; + card->mpa_rx.buf_size = 0; } return ret; -- cgit v0.10.2 From 9a9053c3420fcc5779319e4ea267c6dcbf8d7c04 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Fri, 18 Sep 2015 06:32:06 -0700 Subject: mwifiex: fix driver init failure under memory pressure 64k Tx and Rx buffers are allocated during driver initialization for SDIO level data aggregations. When host is under memory pressure situation, kzalloc() request for 64k may fail. We will try allocating 32k buffers and disable our rx single port aggreagation feature in this situation. If the allocation still fails, we will disable our sdio multport aggregation feature as well. In this way, we will transmit and receive packets one by one, thus reduce the demand for big memory. Signed-off-by: Xinming Hu Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 96663214..2c2004e 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -860,6 +860,8 @@ struct mwifiex_adapter { u8 more_task_flag; u16 tx_buf_size; u16 curr_tx_buf_size; + /* sdio single port rx aggregation capability */ + bool host_disable_sdio_rx_aggr; bool sdio_rx_aggr_enable; u16 sdio_rx_block_size; u32 ioport; diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index d35210c..78a8474 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -2058,16 +2058,26 @@ static int mwifiex_init_sdio(struct mwifiex_adapter *adapter) ret = mwifiex_alloc_sdio_mpa_buffers(adapter, card->mp_tx_agg_buf_size, card->mp_rx_agg_buf_size); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "failed to alloc sdio mp-a buffers\n"); - kfree(card->mp_regs); - return -1; + + /* Allocate 32k MPA Tx/Rx buffers if 64k memory allocation fails */ + if (ret && (card->mp_tx_agg_buf_size == MWIFIEX_MP_AGGR_BUF_SIZE_MAX || + card->mp_rx_agg_buf_size == MWIFIEX_MP_AGGR_BUF_SIZE_MAX)) { + /* Disable rx single port aggregation */ + adapter->host_disable_sdio_rx_aggr = true; + + ret = mwifiex_alloc_sdio_mpa_buffers + (adapter, MWIFIEX_MP_AGGR_BUF_SIZE_32K, + MWIFIEX_MP_AGGR_BUF_SIZE_32K); + if (ret) { + /* Disable multi port aggregation */ + card->mpa_tx.enabled = 0; + card->mpa_rx.enabled = 0; + } } adapter->auto_tdls = card->can_auto_tdls; adapter->ext_scan = card->can_ext_scan; - return ret; + return 0; } /* diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index a49a80d..504b321 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -2125,7 +2125,8 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) /** Set SDIO Single Port RX Aggr Info */ if (priv->adapter->iface_type == MWIFIEX_SDIO && - ISSUPP_SDIO_SPA_ENABLED(priv->adapter->fw_cap_info)) { + ISSUPP_SDIO_SPA_ENABLED(priv->adapter->fw_cap_info) && + !priv->adapter->host_disable_sdio_rx_aggr) { sdio_sp_rx_aggr_enable = true; ret = mwifiex_send_cmd(priv, HostCmd_CMD_SDIO_SP_RX_AGGR_CFG, -- cgit v0.10.2 From 02421dd3f6af1b6becb6d9a690a2b70c73c03a7a Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Fri, 18 Sep 2015 06:32:07 -0700 Subject: mwifiex: NULL check for cfg80211_inform_bss() cfg80211_inform_bss would return null in some cases, such as memory allocation failure. This patch adds sanity check for this case, to avoid possible issues when above corner case is hit. Signed-off-by: Xinming Hu Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index d814340..30cbafb 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1994,8 +1994,10 @@ static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv) CFG80211_BSS_FTYPE_UNKNOWN, bss_info.bssid, 0, WLAN_CAPABILITY_IBSS, 0, ie_buf, ie_len, 0, GFP_KERNEL); - cfg80211_put_bss(priv->wdev.wiphy, bss); - memcpy(priv->cfg_bssid, bss_info.bssid, ETH_ALEN); + if (bss) { + cfg80211_put_bss(priv->wdev.wiphy, bss); + ether_addr_copy(priv->cfg_bssid, bss_info.bssid); + } return 0; } diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index 5847863..3675730 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -1839,14 +1839,18 @@ mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info, bssid, timestamp, cap_info_bitmap, beacon_period, ie_buf, ie_len, rssi, GFP_KERNEL); - bss_priv = (struct mwifiex_bss_priv *)bss->priv; - bss_priv->band = band; - bss_priv->fw_tsf = fw_tsf; - if (priv->media_connected && - !memcmp(bssid, priv->curr_bss_params.bss_descriptor - .mac_address, ETH_ALEN)) - mwifiex_update_curr_bss_params(priv, bss); - cfg80211_put_bss(priv->wdev.wiphy, bss); + if (bss) { + bss_priv = (struct mwifiex_bss_priv *)bss->priv; + bss_priv->band = band; + bss_priv->fw_tsf = fw_tsf; + if (priv->media_connected && + !memcmp(bssid, priv->curr_bss_params. + bss_descriptor.mac_address, + ETH_ALEN)) + mwifiex_update_curr_bss_params(priv, + bss); + cfg80211_put_bss(priv->wdev.wiphy, bss); + } if ((chan->flags & IEEE80211_CHAN_RADAR) || (chan->flags & IEEE80211_CHAN_NO_IR)) { -- cgit v0.10.2 From 2c3da961268ff7f4a6c958fee081c58aef2e5c1d Mon Sep 17 00:00:00 2001 From: Aniket Nagarnaik Date: Fri, 18 Sep 2015 06:32:08 -0700 Subject: mwifiex: don't always include ht/vht info in tdls confirm frame Current TDLS implementation always includes ht/vht information in tdls setup confirm frame which causes teardown by legacy peer station after TDLS handshake. We will inclue ht/vht capabilities in tdls setup confirm frame only if peer station supports it to fix this problem. Signed-off-by: Aniket Nagarnaik Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c index b3e163d..9275f9c 100644 --- a/drivers/net/wireless/mwifiex/tdls.c +++ b/drivers/net/wireless/mwifiex/tdls.c @@ -204,6 +204,12 @@ mwifiex_tdls_add_ht_oper(struct mwifiex_private *priv, const u8 *mac, return -1; } + if (!(le16_to_cpu(sta_ptr->tdls_cap.ht_capb.cap_info))) { + mwifiex_dbg(priv->adapter, WARN, + "TDLS peer doesn't support ht capabilities\n"); + return 0; + } + pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_operation) + 2); *pos++ = WLAN_EID_HT_OPERATION; *pos++ = sizeof(struct ieee80211_ht_operation); @@ -252,6 +258,12 @@ static int mwifiex_tdls_add_vht_oper(struct mwifiex_private *priv, return -1; } + if (!(le32_to_cpu(sta_ptr->tdls_cap.vhtcap.vht_cap_info))) { + mwifiex_dbg(adapter, WARN, + "TDLS peer doesn't support vht capabilities\n"); + return 0; + } + if (!mwifiex_is_bss_in_11ac_mode(priv)) { if (sta_ptr->tdls_cap.extcap.ext_capab[7] & WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) { -- cgit v0.10.2 From 17e524b1b60f4390d24a51d9524d1648cf5d1447 Mon Sep 17 00:00:00 2001 From: Aniket Nagarnaik Date: Fri, 18 Sep 2015 06:32:09 -0700 Subject: mwifiex: fix NULL pointer dereference during hidden SSID scan This NULL pointer dereference is observed during suspend resume stress test. All pending commands are cancelled when system goes into suspend state. There a corner case in which host may receive response for last scan command after this and try to trigger extra active scan for hidden SSIDs. The issue is fixed by adding a NULL check to skip that extra scan. Fixes: 2375fa2b36feaf34 (mwifiex: fix unable to connect hidden SSID..) Cc: [v4.2+] Signed-off-by: Aniket Nagarnaik Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index 3675730..c20017c 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -1893,7 +1893,7 @@ mwifiex_active_scan_req_for_passive_chan(struct mwifiex_private *priv) u8 id = 0; struct mwifiex_user_scan_cfg *user_scan_cfg; - if (adapter->active_scan_triggered) { + if (adapter->active_scan_triggered || !priv->scan_request) { adapter->active_scan_triggered = false; return 0; } -- cgit v0.10.2 From 14d9c11c91a606fed65eaae2455423a23bb4ae59 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 18 Sep 2015 06:32:10 -0700 Subject: mwifiex: avoid memsetting PCIe event buffer Preallocated PCIe buffer is being reused for all PCIe interface events. Physical address of the buffer is shared with firmware so that it can perform DMA on it. As event length is specified in the header, there should not be a problem if the buffer gets overwritten. We will save some cycles by avoiding memset everytime while submitting the buffer to firmware. Fixes: 2728cecdc7d6bf3d21(mwifiex: corrections in PCIe event skb) Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 408b684..21192b6 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -1815,7 +1815,6 @@ static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter, if (!card->evt_buf_list[rdptr]) { skb_push(skb, INTF_HEADER_LEN); skb_put(skb, MAX_EVENT_SIZE - skb->len); - memset(skb->data, 0, MAX_EVENT_SIZE); if (mwifiex_map_pci_memory(adapter, skb, MAX_EVENT_SIZE, PCI_DMA_FROMDEVICE)) -- cgit v0.10.2 From 70c5ad7e20c1426d14f6e722da0972b2c9163db7 Mon Sep 17 00:00:00 2001 From: Zhaoyang Liu Date: Fri, 18 Sep 2015 06:32:11 -0700 Subject: mwifiex: correct paused tx data packet counter This patch fixes observed issues while updating counter for number of paused data packets in wmm queue when trying to delete packet or delete station entry. Signed-off-by: Zhaoyang Liu Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/uap_txrx.c b/drivers/net/wireless/mwifiex/uap_txrx.c index 8766741..74d5d72 100644 --- a/drivers/net/wireless/mwifiex/uap_txrx.c +++ b/drivers/net/wireless/mwifiex/uap_txrx.c @@ -31,7 +31,8 @@ */ static bool mwifiex_uap_del_tx_pkts_in_ralist(struct mwifiex_private *priv, - struct list_head *ra_list_head) + struct list_head *ra_list_head, + int tid) { struct mwifiex_ra_list_tbl *ra_list; struct sk_buff *skb, *tmp; @@ -49,7 +50,10 @@ mwifiex_uap_del_tx_pkts_in_ralist(struct mwifiex_private *priv, __skb_unlink(skb, &ra_list->skb_head); mwifiex_write_data_complete(adapter, skb, 0, -1); - atomic_dec(&priv->wmm.tx_pkts_queued); + if (ra_list->tx_paused) + priv->wmm.pkts_paused[tid]--; + else + atomic_dec(&priv->wmm.tx_pkts_queued); pkt_deleted = true; } if ((atomic_read(&adapter->pending_bridged_pkts) <= @@ -77,7 +81,7 @@ static void mwifiex_uap_cleanup_tx_queues(struct mwifiex_private *priv) if (priv->del_list_idx == MAX_NUM_TID) priv->del_list_idx = 0; ra_list = &priv->wmm.tid_tbl_ptr[priv->del_list_idx].ra_list; - if (mwifiex_uap_del_tx_pkts_in_ralist(priv, ra_list)) { + if (mwifiex_uap_del_tx_pkts_in_ralist(priv, ra_list, i)) { priv->del_list_idx++; break; } diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 173d366..8e30fb3 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -160,7 +160,6 @@ void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra) ra_list->tdls_link = false; ra_list->ba_status = BA_SETUP_NONE; ra_list->amsdu_in_ampdu = false; - ra_list->tx_paused = false; if (!mwifiex_queuing_ra_based(priv)) { if (mwifiex_is_tdls_link_setup (mwifiex_get_tdls_link_status(priv, ra))) { @@ -173,6 +172,8 @@ void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra) } else { spin_lock_irqsave(&priv->sta_list_spinlock, flags); node = mwifiex_get_sta_entry(priv, ra); + if (node) + ra_list->tx_paused = node->tx_pause; ra_list->is_11n_enabled = mwifiex_is_sta_11n_enabled(priv, node); if (ra_list->is_11n_enabled) @@ -737,7 +738,11 @@ mwifiex_wmm_del_peer_ra_list(struct mwifiex_private *priv, const u8 *ra_addr) if (!ra_list) continue; mwifiex_wmm_del_pkts_in_ralist_node(priv, ra_list); - atomic_sub(ra_list->total_pkt_count, &priv->wmm.tx_pkts_queued); + if (ra_list->tx_paused) + priv->wmm.pkts_paused[i] -= ra_list->total_pkt_count; + else + atomic_sub(ra_list->total_pkt_count, + &priv->wmm.tx_pkts_queued); list_del(&ra_list->list); kfree(ra_list); } -- cgit v0.10.2 From 17090beec26ea5bd064c67e2707151722d9d88d6 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 18 Sep 2015 06:32:12 -0700 Subject: mwifiex: Suppress -ENOSR error for data traffic on USB We have preallocated buffer pool for Tx data. During high data traffic, all buffers are submitted to USB and driver needs to wait until one of the buffers get available for next Tx packet. "data: -ENOSR is returned" errors is expected in this case. Let's lower the priority of this message. Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c index 8b1e5b5..98f097d 100644 --- a/drivers/net/wireless/mwifiex/txrx.c +++ b/drivers/net/wireless/mwifiex/txrx.c @@ -130,7 +130,7 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, switch (ret) { case -ENOSR: - mwifiex_dbg(adapter, ERROR, "data: -ENOSR is returned\n"); + mwifiex_dbg(adapter, DATA, "data: -ENOSR is returned\n"); break; case -EBUSY: if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && -- cgit v0.10.2 From d22871db0172eba2bba2e7ff8373f682c9b001cd Mon Sep 17 00:00:00 2001 From: Zhaoyang Liu Date: Fri, 18 Sep 2015 06:32:13 -0700 Subject: mwifiex: fix tx data_sent issue for usb interface This patch fix missing tx data_sent flag update for usb interface. Except USB interface, data_sent flag has been updated in specific file such as sdio.c and pcie.c. So only USB interface type need check when TX data completed. Signed-off-by: Zhaoyang Liu Signed-off-by: Cathy Luo Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c index 6cb0d33..ce22cc8 100644 --- a/drivers/net/wireless/mwifiex/11n_aggr.c +++ b/drivers/net/wireless/mwifiex/11n_aggr.c @@ -300,7 +300,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); break; case -1: - if (adapter->iface_type != MWIFIEX_PCIE) + if (adapter->iface_type == MWIFIEX_USB) adapter->data_sent = false; mwifiex_dbg(adapter, ERROR, "%s: host_to_card failed: %#x\n", __func__, ret); @@ -308,7 +308,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, mwifiex_write_data_complete(adapter, skb_aggr, 1, ret); return 0; case -EINPROGRESS: - if (adapter->iface_type != MWIFIEX_PCIE) + if (adapter->iface_type == MWIFIEX_USB) adapter->data_sent = false; break; case 0: diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/mwifiex/sta_tx.c index 355ac59..7a4d5f4 100644 --- a/drivers/net/wireless/mwifiex/sta_tx.c +++ b/drivers/net/wireless/mwifiex/sta_tx.c @@ -174,6 +174,7 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) local_tx_pd->bss_type = priv->bss_type; if (adapter->iface_type == MWIFIEX_USB) { + adapter->data_sent = true; ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_DATA, skb, NULL); } else { @@ -191,7 +192,8 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) adapter->dbg.num_tx_host_to_card_failure++; break; case -1: - adapter->data_sent = false; + if (adapter->iface_type == MWIFIEX_USB) + adapter->data_sent = false; dev_kfree_skb_any(skb); mwifiex_dbg(adapter, ERROR, "%s: host_to_card failed: ret=%d\n", @@ -206,6 +208,8 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) adapter->tx_lock_flag = true; break; case -EINPROGRESS: + if (adapter->iface_type == MWIFIEX_USB) + adapter->data_sent = false; adapter->tx_lock_flag = true; break; default: diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c index 98f097d..6ef030a 100644 --- a/drivers/net/wireless/mwifiex/txrx.c +++ b/drivers/net/wireless/mwifiex/txrx.c @@ -142,7 +142,7 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); break; case -1: - if (adapter->iface_type != MWIFIEX_PCIE) + if (adapter->iface_type == MWIFIEX_USB) adapter->data_sent = false; mwifiex_dbg(adapter, ERROR, "mwifiex_write_data_async failed: 0x%X\n", @@ -151,7 +151,7 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, mwifiex_write_data_complete(adapter, skb, 0, ret); break; case -EINPROGRESS: - if (adapter->iface_type != MWIFIEX_PCIE) + if (adapter->iface_type == MWIFIEX_USB) adapter->data_sent = false; break; case 0: @@ -222,7 +222,7 @@ static int mwifiex_host_to_card(struct mwifiex_adapter *adapter, mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); break; case -1: - if (adapter->iface_type != MWIFIEX_PCIE) + if (adapter->iface_type == MWIFIEX_USB) adapter->data_sent = false; mwifiex_dbg(adapter, ERROR, "mwifiex_write_data_async failed: 0x%X\n", ret); @@ -230,7 +230,7 @@ static int mwifiex_host_to_card(struct mwifiex_adapter *adapter, mwifiex_write_data_complete(adapter, skb, 0, ret); break; case -EINPROGRESS: - if (adapter->iface_type != MWIFIEX_PCIE) + if (adapter->iface_type == MWIFIEX_USB) adapter->data_sent = false; break; case 0: diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 8e30fb3..8a3f59e 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -1356,14 +1356,14 @@ mwifiex_send_processed_packet(struct mwifiex_private *priv, ra_list_flags); break; case -1: - if (adapter->iface_type != MWIFIEX_PCIE) + if (adapter->iface_type == MWIFIEX_USB) adapter->data_sent = false; mwifiex_dbg(adapter, ERROR, "host_to_card failed: %#x\n", ret); adapter->dbg.num_tx_host_to_card_failure++; mwifiex_write_data_complete(adapter, skb, 0, ret); break; case -EINPROGRESS: - if (adapter->iface_type != MWIFIEX_PCIE) + if (adapter->iface_type == MWIFIEX_USB) adapter->data_sent = false; break; case 0: -- cgit v0.10.2 From d941444321f6b84bdd36d8d24eef10171c9ab981 Mon Sep 17 00:00:00 2001 From: Zhaoyang Liu Date: Fri, 18 Sep 2015 06:32:14 -0700 Subject: mwifiex: move usb specific data_sent update to usb.c This patch move data_sent flag update to usb.c file. >From now all data_sent update cases only happened in specific file: sdio.c, usb.c, pcie.c. Outside ot these files, it is only allowed to check the value of data_ent. Signed-off-by: Zhaoyang Liu Signed-off-by: Cathy Luo Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c index ce22cc8..46221c2 100644 --- a/drivers/net/wireless/mwifiex/11n_aggr.c +++ b/drivers/net/wireless/mwifiex/11n_aggr.c @@ -259,7 +259,6 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, } if (adapter->iface_type == MWIFIEX_USB) { - adapter->data_sent = true; ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_DATA, skb_aggr, NULL); } else { @@ -300,16 +299,12 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); break; case -1: - if (adapter->iface_type == MWIFIEX_USB) - adapter->data_sent = false; mwifiex_dbg(adapter, ERROR, "%s: host_to_card failed: %#x\n", __func__, ret); adapter->dbg.num_tx_host_to_card_failure++; mwifiex_write_data_complete(adapter, skb_aggr, 1, ret); return 0; case -EINPROGRESS: - if (adapter->iface_type == MWIFIEX_USB) - adapter->data_sent = false; break; case 0: mwifiex_write_data_complete(adapter, skb_aggr, 1, ret); diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/mwifiex/sta_tx.c index 7a4d5f4..877ad06 100644 --- a/drivers/net/wireless/mwifiex/sta_tx.c +++ b/drivers/net/wireless/mwifiex/sta_tx.c @@ -174,7 +174,6 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) local_tx_pd->bss_type = priv->bss_type; if (adapter->iface_type == MWIFIEX_USB) { - adapter->data_sent = true; ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_DATA, skb, NULL); } else { @@ -192,8 +191,6 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) adapter->dbg.num_tx_host_to_card_failure++; break; case -1: - if (adapter->iface_type == MWIFIEX_USB) - adapter->data_sent = false; dev_kfree_skb_any(skb); mwifiex_dbg(adapter, ERROR, "%s: host_to_card failed: ret=%d\n", @@ -208,8 +205,6 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) adapter->tx_lock_flag = true; break; case -EINPROGRESS: - if (adapter->iface_type == MWIFIEX_USB) - adapter->data_sent = false; adapter->tx_lock_flag = true; break; default: diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c index 6ef030a..90f4915 100644 --- a/drivers/net/wireless/mwifiex/txrx.c +++ b/drivers/net/wireless/mwifiex/txrx.c @@ -115,7 +115,6 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) local_tx_pd = (struct txpd *)(head_ptr + hroom); if (adapter->iface_type == MWIFIEX_USB) { - adapter->data_sent = true; ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_DATA, skb, NULL); @@ -142,8 +141,6 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); break; case -1: - if (adapter->iface_type == MWIFIEX_USB) - adapter->data_sent = false; mwifiex_dbg(adapter, ERROR, "mwifiex_write_data_async failed: 0x%X\n", ret); @@ -151,8 +148,6 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, mwifiex_write_data_complete(adapter, skb, 0, ret); break; case -EINPROGRESS: - if (adapter->iface_type == MWIFIEX_USB) - adapter->data_sent = false; break; case 0: mwifiex_write_data_complete(adapter, skb, 0, ret); @@ -193,7 +188,6 @@ static int mwifiex_host_to_card(struct mwifiex_adapter *adapter, } if (adapter->iface_type == MWIFIEX_USB) { - adapter->data_sent = true; ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_DATA, skb, NULL); @@ -222,16 +216,12 @@ static int mwifiex_host_to_card(struct mwifiex_adapter *adapter, mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); break; case -1: - if (adapter->iface_type == MWIFIEX_USB) - adapter->data_sent = false; mwifiex_dbg(adapter, ERROR, "mwifiex_write_data_async failed: 0x%X\n", ret); adapter->dbg.num_tx_host_to_card_failure++; mwifiex_write_data_complete(adapter, skb, 0, ret); break; case -EINPROGRESS: - if (adapter->iface_type == MWIFIEX_USB) - adapter->data_sent = false; break; case 0: mwifiex_write_data_complete(adapter, skb, 0, ret); @@ -306,9 +296,6 @@ int mwifiex_write_data_complete(struct mwifiex_adapter *adapter, if (!priv) goto done; - if (adapter->iface_type == MWIFIEX_USB) - adapter->data_sent = false; - mwifiex_set_trans_start(priv->netdev); if (!status) { priv->stats.tx_packets++; diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c index 5e789b2..01438a4 100644 --- a/drivers/net/wireless/mwifiex/usb.c +++ b/drivers/net/wireless/mwifiex/usb.c @@ -277,6 +277,7 @@ static void mwifiex_usb_tx_complete(struct urb *urb) mwifiex_dbg(adapter, DATA, "%s: DATA\n", __func__); atomic_dec(&card->tx_data_urb_pending); + adapter->data_sent = false; mwifiex_write_data_complete(adapter, context->skb, 0, urb->status ? -1 : 0); } @@ -759,6 +760,7 @@ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, if (ep == card->tx_data_ep && atomic_read(&card->tx_data_urb_pending) >= MWIFIEX_TX_DATA_URB) { + adapter->data_sent = true; return -EBUSY; } @@ -795,6 +797,7 @@ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, atomic_dec(&card->tx_cmd_urb_pending); } else { atomic_dec(&card->tx_data_urb_pending); + adapter->data_sent = false; if (card->tx_data_ix) card->tx_data_ix--; else @@ -805,8 +808,10 @@ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, } else { if (ep == card->tx_data_ep && atomic_read(&card->tx_data_urb_pending) == - MWIFIEX_TX_DATA_URB) + MWIFIEX_TX_DATA_URB) { + adapter->data_sent = true; return -ENOSR; + } } return -EINPROGRESS; diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 8a3f59e..4f303f3 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -1326,7 +1326,6 @@ mwifiex_send_processed_packet(struct mwifiex_private *priv, spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); if (adapter->iface_type == MWIFIEX_USB) { - adapter->data_sent = true; ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_DATA, skb, NULL); } else { @@ -1356,15 +1355,11 @@ mwifiex_send_processed_packet(struct mwifiex_private *priv, ra_list_flags); break; case -1: - if (adapter->iface_type == MWIFIEX_USB) - adapter->data_sent = false; mwifiex_dbg(adapter, ERROR, "host_to_card failed: %#x\n", ret); adapter->dbg.num_tx_host_to_card_failure++; mwifiex_write_data_complete(adapter, skb, 0, ret); break; case -EINPROGRESS: - if (adapter->iface_type == MWIFIEX_USB) - adapter->data_sent = false; break; case 0: mwifiex_write_data_complete(adapter, skb, 0, ret); -- cgit v0.10.2 From 2b0f997db43f01370503d88b5fbec9a350a7955f Mon Sep 17 00:00:00 2001 From: Zhaoyang Liu Date: Fri, 18 Sep 2015 06:32:15 -0700 Subject: mwifiex: add usb multi channel event process support This patch add multi channel event process for USB multi tx data endpoints. Driver receives firmware multi channel event only in case that new connection is setup or interface is disconnect. Different BSS interface need update used usb endpoint. Signed-off-by: Zhaoyang Liu Signed-off-by: Cathy Luo Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 3ec2ac8..0e6f029 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -104,6 +104,7 @@ enum KEY_TYPE_ID { enum mwifiex_usb_ep { MWIFIEX_USB_EP_CMD_EVENT = 1, MWIFIEX_USB_EP_DATA = 2, + MWIFIEX_USB_EP_DATA_CH2 = 3, }; enum MWIFIEX_802_11_PRIVACY_FILTER { @@ -173,6 +174,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_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) #define TLV_TYPE_SCAN_CHANNEL_GAP (PROPRIETARY_TLV_BASE_ID + 197) #define TLV_TYPE_API_REV (PROPRIETARY_TLV_BASE_ID + 199) @@ -1984,6 +1986,22 @@ struct mwifiex_ie_types_multi_chan_info { u8 tlv_buffer[0]; } __packed; +struct mwifiex_ie_types_mc_group_info { + struct mwifiex_ie_types_header header; + u8 chan_group_id; + u8 chan_buf_weight; + u8 band_config; + u8 chan_num; + u32 chan_time; + u32 reserved; + union { + u8 sdio_func_num; + u8 usb_ep_num; + } hid_num; + u8 intf_num; + u8 bss_type_numlist[0]; +} __packed; + struct meas_rpt_map { u8 rssi:3; u8 unmeasured:1; diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index 5d3ae63..de74a77 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -78,6 +78,7 @@ int mwifiex_init_priv(struct mwifiex_private *priv) priv->media_connected = false; eth_broadcast_addr(priv->curr_addr); priv->port_open = false; + priv->usb_port = MWIFIEX_USB_EP_DATA; priv->pkt_tx_ctrl = 0; priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; priv->data_rate = 0; /* Initially indicate the rate as auto */ diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 2c2004e..9772a18 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -519,6 +519,7 @@ struct mwifiex_private { u8 curr_addr[ETH_ALEN]; u8 media_connected; u8 port_open; + u8 usb_port; u32 num_tx_timeout; /* track consecutive timeout */ u8 tx_timeout_cnt; @@ -989,6 +990,7 @@ struct mwifiex_adapter { u8 coex_rx_win_size; bool drcs_enabled; u8 active_scan_triggered; + bool usb_mc_status; }; void mwifiex_process_tx_queue(struct mwifiex_adapter *adapter); diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c index 3d18c58..6792089 100644 --- a/drivers/net/wireless/mwifiex/sta_event.c +++ b/drivers/net/wireless/mwifiex/sta_event.c @@ -313,25 +313,74 @@ void mwifiex_process_multi_chan_event(struct mwifiex_private *priv, struct sk_buff *event_skb) { struct mwifiex_ie_types_multi_chan_info *chan_info; - u16 status; + struct mwifiex_ie_types_mc_group_info *grp_info; + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_ie_types_header *tlv; + u16 tlv_buf_left, tlv_type, tlv_len; + int intf_num, bss_type, bss_num, i; + struct mwifiex_private *intf_priv; + tlv_buf_left = event_skb->len - sizeof(u32); chan_info = (void *)event_skb->data + sizeof(u32); - if (le16_to_cpu(chan_info->header.type) != TLV_TYPE_MULTI_CHAN_INFO) { - mwifiex_dbg(priv->adapter, ERROR, + if (le16_to_cpu(chan_info->header.type) != TLV_TYPE_MULTI_CHAN_INFO || + tlv_buf_left < sizeof(struct mwifiex_ie_types_multi_chan_info)) { + mwifiex_dbg(adapter, ERROR, "unknown TLV in chan_info event\n"); return; } - status = le16_to_cpu(chan_info->status); + adapter->usb_mc_status = le16_to_cpu(chan_info->status); + mwifiex_dbg(adapter, EVENT, "multi chan operation %s\n", + adapter->usb_mc_status ? "started" : "over"); - if (status) { - mwifiex_dbg(priv->adapter, EVENT, - "multi-channel operation started\n"); - } else { - mwifiex_dbg(priv->adapter, EVENT, - "multi-channel operation over\n"); + tlv_buf_left -= sizeof(struct mwifiex_ie_types_multi_chan_info); + tlv = (struct mwifiex_ie_types_header *)chan_info->tlv_buffer; + + while (tlv_buf_left >= (int)sizeof(struct mwifiex_ie_types_header)) { + tlv_type = le16_to_cpu(tlv->type); + tlv_len = le16_to_cpu(tlv->len); + if ((sizeof(struct mwifiex_ie_types_header) + tlv_len) > + tlv_buf_left) { + mwifiex_dbg(adapter, ERROR, "wrong tlv: tlvLen=%d,\t" + "tlvBufLeft=%d\n", tlv_len, tlv_buf_left); + break; + } + if (tlv_type != TLV_TYPE_MC_GROUP_INFO) { + mwifiex_dbg(adapter, ERROR, "wrong tlv type: 0x%x\n", + tlv_type); + break; + } + + grp_info = (struct mwifiex_ie_types_mc_group_info *)tlv; + intf_num = grp_info->intf_num; + for (i = 0; i < intf_num; i++) { + bss_type = grp_info->bss_type_numlist[i] >> 4; + bss_num = grp_info->bss_type_numlist[i] & BSS_NUM_MASK; + intf_priv = mwifiex_get_priv_by_id(adapter, bss_num, + bss_type); + if (!intf_priv) { + mwifiex_dbg(adapter, ERROR, + "Invalid bss_type bss_num\t" + "in multi channel event\n"); + continue; + } + if (adapter->iface_type == MWIFIEX_USB) { + u8 ep; + + ep = grp_info->hid_num.usb_ep_num; + if (ep == MWIFIEX_USB_EP_DATA || + ep == MWIFIEX_USB_EP_DATA_CH2) + intf_priv->usb_port = ep; + } + } + + tlv_buf_left -= sizeof(struct mwifiex_ie_types_header) + + tlv_len; + tlv = (void *)((u8 *)tlv + tlv_len + + sizeof(struct mwifiex_ie_types_header)); } + } void mwifiex_process_tx_pause_event(struct mwifiex_private *priv, diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c index 01438a4..54c9b45 100644 --- a/drivers/net/wireless/mwifiex/usb.c +++ b/drivers/net/wireless/mwifiex/usb.c @@ -858,6 +858,8 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) break; } + adapter->usb_mc_status = false; + return 0; } -- cgit v0.10.2 From 308fe29ef24394e4db66d80c7a23e3e5640aca74 Mon Sep 17 00:00:00 2001 From: Zhaoyang Liu Date: Fri, 18 Sep 2015 06:32:16 -0700 Subject: mwifiex: add usb tx data multi endpoints support This patch add support for USB interface to TX data with different endpoint. And previous TX information are saved in new designed structure. Signed-off-by: Zhaoyang Liu Signed-off-by: Cathy Luo Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 4fa8ca3..4b2110d 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -963,8 +963,10 @@ void mwifiex_drv_info_dump(struct mwifiex_adapter *adapter) cardp = (struct usb_card_rec *)adapter->card; p += sprintf(p, "tx_cmd_urb_pending = %d\n", atomic_read(&cardp->tx_cmd_urb_pending)); - p += sprintf(p, "tx_data_urb_pending = %d\n", - atomic_read(&cardp->tx_data_urb_pending)); + p += sprintf(p, "tx_data_urb_pending_port_0 = %d\n", + atomic_read(&cardp->port[0].tx_data_urb_pending)); + p += sprintf(p, "tx_data_urb_pending_port_1 = %d\n", + atomic_read(&cardp->port[1].tx_data_urb_pending)); p += sprintf(p, "rx_cmd_urb_pending = %d\n", atomic_read(&cardp->rx_cmd_urb_pending)); p += sprintf(p, "rx_data_urb_pending = %d\n", diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c index 54c9b45..df7e7df 100644 --- a/drivers/net/wireless/mwifiex/usb.c +++ b/drivers/net/wireless/mwifiex/usb.c @@ -264,6 +264,8 @@ static void mwifiex_usb_tx_complete(struct urb *urb) struct urb_context *context = (struct urb_context *)(urb->context); struct mwifiex_adapter *adapter = context->adapter; struct usb_card_rec *card = adapter->card; + struct usb_tx_data_port *port; + int i; mwifiex_dbg(adapter, INFO, "%s: status: %d\n", __func__, urb->status); @@ -276,7 +278,13 @@ static void mwifiex_usb_tx_complete(struct urb *urb) } else { mwifiex_dbg(adapter, DATA, "%s: DATA\n", __func__); - atomic_dec(&card->tx_data_urb_pending); + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { + port = &card->port[i]; + if (context->ep == port->tx_data_ep) { + atomic_dec(&port->tx_data_urb_pending); + break; + } + } adapter->data_sent = false; mwifiex_write_data_complete(adapter, context->skb, 0, urb->status ? -1 : 0); @@ -328,7 +336,8 @@ static int mwifiex_usb_submit_rx_urb(struct urb_context *ctx, int size) static void mwifiex_usb_free(struct usb_card_rec *card) { - int i; + struct usb_tx_data_port *port; + int i, j; if (atomic_read(&card->rx_cmd_urb_pending) && card->rx_cmd.urb) usb_kill_urb(card->rx_cmd.urb); @@ -346,9 +355,12 @@ static void mwifiex_usb_free(struct usb_card_rec *card) card->rx_data_list[i].urb = NULL; } - for (i = 0; i < MWIFIEX_TX_DATA_URB; i++) { - usb_free_urb(card->tx_data_list[i].urb); - card->tx_data_list[i].urb = NULL; + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { + port = &card->port[i]; + for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) { + usb_free_urb(port->tx_data_list[j].urb); + port->tx_data_list[j].urb = NULL; + } } usb_free_urb(card->tx_cmd.urb); @@ -438,8 +450,18 @@ static int mwifiex_usb_probe(struct usb_interface *intf, pr_debug("info: bulk OUT: max pkt size: %d, addr: %d\n", le16_to_cpu(epd->wMaxPacketSize), epd->bEndpointAddress); - card->tx_data_ep = usb_endpoint_num(epd); - atomic_set(&card->tx_data_urb_pending, 0); + card->port[0].tx_data_ep = usb_endpoint_num(epd); + atomic_set(&card->port[0].tx_data_urb_pending, 0); + } + if (usb_endpoint_dir_out(epd) && + usb_endpoint_num(epd) == MWIFIEX_USB_EP_DATA_CH2 && + usb_endpoint_xfer_bulk(epd)) { + pr_debug("info: bulk OUT chan2:\t" + "max pkt size: %d, addr: %d\n", + le16_to_cpu(epd->wMaxPacketSize), + epd->bEndpointAddress); + card->port[1].tx_data_ep = usb_endpoint_num(epd); + atomic_set(&card->port[1].tx_data_urb_pending, 0); } if (usb_endpoint_dir_out(epd) && usb_endpoint_num(epd) == MWIFIEX_USB_EP_CMD_EVENT && @@ -481,7 +503,8 @@ static int mwifiex_usb_suspend(struct usb_interface *intf, pm_message_t message) { struct usb_card_rec *card = usb_get_intfdata(intf); struct mwifiex_adapter *adapter; - int i; + struct usb_tx_data_port *port; + int i, j; if (!card || !card->adapter) { pr_err("%s: card or card->adapter is NULL\n", __func__); @@ -512,9 +535,13 @@ static int mwifiex_usb_suspend(struct usb_interface *intf, pm_message_t message) if (card->rx_data_list[i].urb) usb_kill_urb(card->rx_data_list[i].urb); - for (i = 0; i < MWIFIEX_TX_DATA_URB; i++) - if (card->tx_data_list[i].urb) - usb_kill_urb(card->tx_data_list[i].urb); + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { + port = &card->port[i]; + for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) { + if (port->tx_data_list[j].urb) + usb_kill_urb(port->tx_data_list[j].urb); + } + } if (card->tx_cmd.urb) usb_kill_urb(card->tx_cmd.urb); @@ -626,7 +653,8 @@ static struct usb_driver mwifiex_usb_driver = { static int mwifiex_usb_tx_init(struct mwifiex_adapter *adapter) { struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; - int i; + struct usb_tx_data_port *port; + int i, j; card->tx_cmd.adapter = adapter; card->tx_cmd.ep = card->tx_cmd_ep; @@ -638,17 +666,21 @@ static int mwifiex_usb_tx_init(struct mwifiex_adapter *adapter) return -ENOMEM; } - card->tx_data_ix = 0; - - for (i = 0; i < MWIFIEX_TX_DATA_URB; i++) { - card->tx_data_list[i].adapter = adapter; - card->tx_data_list[i].ep = card->tx_data_ep; - - card->tx_data_list[i].urb = usb_alloc_urb(0, GFP_KERNEL); - if (!card->tx_data_list[i].urb) { - mwifiex_dbg(adapter, ERROR, - "tx_data_list[] urb allocation failed\n"); - return -ENOMEM; + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { + port = &card->port[i]; + if (!port->tx_data_ep) + continue; + port->tx_data_ix = 0; + for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) { + port->tx_data_list[j].adapter = adapter; + port->tx_data_list[j].ep = port->tx_data_ep; + port->tx_data_list[j].urb = + usb_alloc_urb(0, GFP_KERNEL); + if (!port->tx_data_list[j].urb) { + mwifiex_dbg(adapter, ERROR, + "urb allocation failed\n"); + return -ENOMEM; + } } } @@ -743,9 +775,11 @@ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, struct mwifiex_tx_param *tx_param) { struct usb_card_rec *card = adapter->card; - struct urb_context *context; + struct urb_context *context = NULL; + struct usb_tx_data_port *port = NULL; u8 *data = (u8 *)skb->data; struct urb *tx_urb; + int idx; if (adapter->is_suspended) { mwifiex_dbg(adapter, ERROR, @@ -758,20 +792,30 @@ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, return -1; } - if (ep == card->tx_data_ep && - atomic_read(&card->tx_data_urb_pending) >= MWIFIEX_TX_DATA_URB) { - adapter->data_sent = true; - return -EBUSY; - } - mwifiex_dbg(adapter, INFO, "%s: ep=%d\n", __func__, ep); if (ep == card->tx_cmd_ep) { context = &card->tx_cmd; } else { - if (card->tx_data_ix >= MWIFIEX_TX_DATA_URB) - card->tx_data_ix = 0; - context = &card->tx_data_list[card->tx_data_ix++]; + for (idx = 0; idx < MWIFIEX_TX_DATA_PORT; idx++) { + if (ep == card->port[idx].tx_data_ep) { + port = &card->port[idx]; + if (atomic_read(&port->tx_data_urb_pending) + >= MWIFIEX_TX_DATA_URB) { + adapter->data_sent = true; + return -EBUSY; + } + if (port->tx_data_ix >= MWIFIEX_TX_DATA_URB) + port->tx_data_ix = 0; + context = + &port->tx_data_list[port->tx_data_ix++]; + break; + } + } + if (!port) { + mwifiex_dbg(adapter, ERROR, "Wrong usb tx data port\n"); + return -1; + } } context->adapter = adapter; @@ -788,7 +832,7 @@ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, if (ep == card->tx_cmd_ep) atomic_inc(&card->tx_cmd_urb_pending); else - atomic_inc(&card->tx_data_urb_pending); + atomic_inc(&port->tx_data_urb_pending); if (usb_submit_urb(tx_urb, GFP_ATOMIC)) { mwifiex_dbg(adapter, ERROR, @@ -796,18 +840,18 @@ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, if (ep == card->tx_cmd_ep) { atomic_dec(&card->tx_cmd_urb_pending); } else { - atomic_dec(&card->tx_data_urb_pending); + atomic_dec(&port->tx_data_urb_pending); adapter->data_sent = false; - if (card->tx_data_ix) - card->tx_data_ix--; + if (port->tx_data_ix) + port->tx_data_ix--; else - card->tx_data_ix = MWIFIEX_TX_DATA_URB; + port->tx_data_ix = MWIFIEX_TX_DATA_URB; } return -1; } else { - if (ep == card->tx_data_ep && - atomic_read(&card->tx_data_urb_pending) == + if (ep != card->tx_cmd_ep && + atomic_read(&port->tx_data_urb_pending) == MWIFIEX_TX_DATA_URB) { adapter->data_sent = true; return -ENOSR; diff --git a/drivers/net/wireless/mwifiex/usb.h b/drivers/net/wireless/mwifiex/usb.h index f0051f8..0a756f2 100644 --- a/drivers/net/wireless/mwifiex/usb.h +++ b/drivers/net/wireless/mwifiex/usb.h @@ -40,6 +40,7 @@ #define USB8XXX_FW_READY 2 #define USB8XXX_FW_MAX_RETRY 3 +#define MWIFIEX_TX_DATA_PORT 2 #define MWIFIEX_TX_DATA_URB 6 #define MWIFIEX_RX_DATA_URB 6 #define MWIFIEX_USB_TIMEOUT 100 @@ -64,6 +65,13 @@ struct urb_context { u8 ep; }; +struct usb_tx_data_port { + u8 tx_data_ep; + atomic_t tx_data_urb_pending; + int tx_data_ix; + struct urb_context tx_data_list[MWIFIEX_TX_DATA_URB]; +}; + struct usb_card_rec { struct mwifiex_adapter *adapter; struct usb_device *udev; @@ -75,14 +83,11 @@ struct usb_card_rec { u8 usb_boot_state; u8 rx_data_ep; atomic_t rx_data_urb_pending; - u8 tx_data_ep; u8 tx_cmd_ep; - atomic_t tx_data_urb_pending; atomic_t tx_cmd_urb_pending; int bulk_out_maxpktsize; struct urb_context tx_cmd; - int tx_data_ix; - struct urb_context tx_data_list[MWIFIEX_TX_DATA_URB]; + struct usb_tx_data_port port[MWIFIEX_TX_DATA_PORT]; }; struct fw_header { -- cgit v0.10.2 From 7e4e5d2cd0817b91eb07c7abe297012bf76616ea Mon Sep 17 00:00:00 2001 From: Zhaoyang Liu Date: Fri, 18 Sep 2015 06:32:17 -0700 Subject: mwifiex: add usb multi endpoints resync support This patch add support for usb multi endpoints resync. Once multi channel event is received from firmware, update usb_mc_setp flag to block TX data until setup is over. And available data endpoint will be attached to BSS interface. Signed-off-by: Zhaoyang Liu Signed-off-by: Cathy Luo Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 4b2110d..6e3faa7 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -294,9 +294,15 @@ process_start: /* We have tried to wakeup the card already */ if (adapter->pm_wakeup_fw_try) break; - if (adapter->ps_state != PS_STATE_AWAKE || - adapter->tx_lock_flag) + if (adapter->ps_state != PS_STATE_AWAKE) break; + if (adapter->tx_lock_flag) { + if (adapter->iface_type == MWIFIEX_USB) { + if (!adapter->usb_mc_setup) + break; + } else + break; + } if ((!adapter->scan_chan_gap_enabled && adapter->scan_processing) || adapter->data_sent || @@ -345,11 +351,18 @@ process_start: */ if ((adapter->ps_state == PS_STATE_SLEEP) || (adapter->ps_state == PS_STATE_PRE_SLEEP) || - (adapter->ps_state == PS_STATE_SLEEP_CFM) || - adapter->tx_lock_flag){ + (adapter->ps_state == PS_STATE_SLEEP_CFM)) { continue; } + if (adapter->tx_lock_flag) { + if (adapter->iface_type == MWIFIEX_USB) { + if (!adapter->usb_mc_setup) + continue; + } else + continue; + } + if (!adapter->cmd_sent && !adapter->curr_cmd && mwifiex_is_send_cmd_allowed (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) { @@ -359,6 +372,13 @@ process_start: } } + /** If USB Multi channel setup ongoing, + * wait for ready to tx data. + */ + if (adapter->iface_type == MWIFIEX_USB && + adapter->usb_mc_setup) + continue; + if ((adapter->scan_chan_gap_enabled || !adapter->scan_processing) && !adapter->data_sent && @@ -928,6 +948,32 @@ mwifiex_tx_timeout(struct net_device *dev) } } +void mwifiex_multi_chan_resync(struct mwifiex_adapter *adapter) +{ + struct usb_card_rec *card = adapter->card; + struct mwifiex_private *priv; + u16 tx_buf_size; + int i, ret; + + card->mc_resync_flag = true; + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { + if (atomic_read(&card->port[i].tx_data_urb_pending)) { + mwifiex_dbg(adapter, WARN, "pending data urb in sys\n"); + return; + } + } + + card->mc_resync_flag = false; + tx_buf_size = 0xffff; + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF, + HostCmd_ACT_GEN_SET, 0, &tx_buf_size, false); + if (ret) + mwifiex_dbg(adapter, ERROR, + "send reconfig tx buf size cmd err\n"); +} +EXPORT_SYMBOL_GPL(mwifiex_multi_chan_resync); + void mwifiex_drv_info_dump(struct mwifiex_adapter *adapter) { void *p; diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 9772a18..8813002 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -816,6 +816,7 @@ struct mwifiex_if_ops { void (*iface_work)(struct work_struct *work); void (*submit_rem_rx_urbs)(struct mwifiex_adapter *adapter); void (*deaggr_pkt)(struct mwifiex_adapter *, struct sk_buff *); + void (*multi_port_resync)(struct mwifiex_adapter *); }; struct mwifiex_adapter { @@ -991,6 +992,7 @@ struct mwifiex_adapter { bool drcs_enabled; u8 active_scan_triggered; bool usb_mc_status; + bool usb_mc_setup; }; void mwifiex_process_tx_queue(struct mwifiex_adapter *adapter); @@ -1564,6 +1566,7 @@ void mwifiex_process_tx_pause_event(struct mwifiex_private *priv, struct sk_buff *event); void mwifiex_process_multi_chan_event(struct mwifiex_private *priv, struct sk_buff *event_skb); +void mwifiex_multi_chan_resync(struct mwifiex_adapter *adapter); #ifdef CONFIG_DEBUG_FS void mwifiex_debugfs_init(void); diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c index 87b69d8..d0961635 100644 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c @@ -1128,6 +1128,17 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, ret = mwifiex_ret_11n_addba_resp(priv, resp); break; case HostCmd_CMD_RECONFIGURE_TX_BUFF: + if (0xffff == (u16)le16_to_cpu(resp->params.tx_buf.buff_size)) { + if (adapter->iface_type == MWIFIEX_USB && + adapter->usb_mc_setup) { + if (adapter->if_ops.multi_port_resync) + adapter->if_ops. + multi_port_resync(adapter); + adapter->usb_mc_setup = false; + adapter->tx_lock_flag = false; + } + break; + } adapter->tx_buf_size = (u16) le16_to_cpu(resp->params. tx_buf.buff_size); adapter->tx_buf_size = (adapter->tx_buf_size diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c index 6792089..c333b24 100644 --- a/drivers/net/wireless/mwifiex/sta_event.c +++ b/drivers/net/wireless/mwifiex/sta_event.c @@ -381,6 +381,11 @@ void mwifiex_process_multi_chan_event(struct mwifiex_private *priv, sizeof(struct mwifiex_ie_types_header)); } + if (adapter->iface_type == MWIFIEX_USB) { + adapter->tx_lock_flag = true; + adapter->usb_mc_setup = true; + mwifiex_multi_chan_resync(adapter); + } } void mwifiex_process_tx_pause_event(struct mwifiex_private *priv, diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c index df7e7df..78ad857 100644 --- a/drivers/net/wireless/mwifiex/usb.c +++ b/drivers/net/wireless/mwifiex/usb.c @@ -290,6 +290,9 @@ static void mwifiex_usb_tx_complete(struct urb *urb) urb->status ? -1 : 0); } + if (card->mc_resync_flag) + mwifiex_multi_chan_resync(adapter); + mwifiex_queue_main_work(adapter); return; @@ -671,6 +674,10 @@ static int mwifiex_usb_tx_init(struct mwifiex_adapter *adapter) if (!port->tx_data_ep) continue; port->tx_data_ix = 0; + if (port->tx_data_ep == MWIFIEX_USB_EP_DATA) + port->block_status = false; + else + port->block_status = true; for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) { port->tx_data_list[j].adapter = adapter; port->tx_data_list[j].ep = port->tx_data_ep; @@ -769,6 +776,53 @@ static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *pbuf, return ret; } +static void mwifiex_usb_port_resync(struct mwifiex_adapter *adapter) +{ + struct usb_card_rec *card = adapter->card; + u8 active_port = MWIFIEX_USB_EP_DATA; + struct mwifiex_private *priv = NULL; + int i; + + if (adapter->usb_mc_status) { + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (!priv) + continue; + if ((priv->bss_role == MWIFIEX_BSS_ROLE_UAP && + !priv->bss_started) || + (priv->bss_role == MWIFIEX_BSS_ROLE_STA && + !priv->media_connected)) + priv->usb_port = MWIFIEX_USB_EP_DATA; + } + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) + card->port[i].block_status = false; + } else { + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (!priv) + continue; + if ((priv->bss_role == MWIFIEX_BSS_ROLE_UAP && + priv->bss_started) || + (priv->bss_role == MWIFIEX_BSS_ROLE_STA && + priv->media_connected)) { + active_port = priv->usb_port; + break; + } + } + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (priv) + priv->usb_port = active_port; + } + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { + if (active_port == card->port[i].tx_data_ep) + card->port[i].block_status = false; + else + card->port[i].block_status = true; + } + } +} + /* This function write a command/data packet to card. */ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, struct sk_buff *skb, @@ -903,6 +957,7 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) } adapter->usb_mc_status = false; + adapter->usb_mc_setup = false; return 0; } @@ -1133,6 +1188,7 @@ static struct mwifiex_if_ops usb_ops = { .event_complete = mwifiex_usb_cmd_event_complete, .host_to_card = mwifiex_usb_host_to_card, .submit_rem_rx_urbs = mwifiex_usb_submit_rem_rx_urbs, + .multi_port_resync = mwifiex_usb_port_resync, }; /* This function initializes the USB driver module. diff --git a/drivers/net/wireless/mwifiex/usb.h b/drivers/net/wireless/mwifiex/usb.h index 0a756f2..bab10ee 100644 --- a/drivers/net/wireless/mwifiex/usb.h +++ b/drivers/net/wireless/mwifiex/usb.h @@ -67,6 +67,7 @@ struct urb_context { struct usb_tx_data_port { u8 tx_data_ep; + u8 block_status; atomic_t tx_data_urb_pending; int tx_data_ix; struct urb_context tx_data_list[MWIFIEX_TX_DATA_URB]; @@ -87,6 +88,7 @@ struct usb_card_rec { atomic_t tx_cmd_urb_pending; int bulk_out_maxpktsize; struct urb_context tx_cmd; + u8 mc_resync_flag; struct usb_tx_data_port port[MWIFIEX_TX_DATA_PORT]; }; -- cgit v0.10.2 From 735ab6bfc03878e6b41feb1f14da750a951854ad Mon Sep 17 00:00:00 2001 From: Zhaoyang Liu Date: Fri, 18 Sep 2015 06:32:18 -0700 Subject: mwifiex: complete usb tx data with multi endpoints This patch do the work to TX data with specific USB endpoint. At the same time, update data_sent flag according to multi port status. And is_port_ready API is added for BSS interface to check if current used usb data endpoint is available or not. Signed-off-by: Zhaoyang Liu Signed-off-by: Cathy Luo Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c index 46221c2..2c5ffa1 100644 --- a/drivers/net/wireless/mwifiex/11n_aggr.c +++ b/drivers/net/wireless/mwifiex/11n_aggr.c @@ -259,7 +259,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, } if (adapter->iface_type == MWIFIEX_USB) { - ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_DATA, + ret = adapter->if_ops.host_to_card(adapter, priv->usb_port, skb_aggr, NULL); } else { if (skb_src) diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 8813002..3959f1c 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -817,6 +817,7 @@ struct mwifiex_if_ops { void (*submit_rem_rx_urbs)(struct mwifiex_adapter *adapter); void (*deaggr_pkt)(struct mwifiex_adapter *, struct sk_buff *); void (*multi_port_resync)(struct mwifiex_adapter *); + bool (*is_port_ready)(struct mwifiex_private *); }; struct mwifiex_adapter { diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c index c333b24..ff3ee9d 100644 --- a/drivers/net/wireless/mwifiex/sta_event.c +++ b/drivers/net/wireless/mwifiex/sta_event.c @@ -616,7 +616,9 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) adapter->tx_lock_flag = false; if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) { if (mwifiex_check_last_packet_indication(priv)) { - if (adapter->data_sent) { + if (adapter->data_sent || + (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv))) { adapter->ps_state = PS_STATE_AWAKE; adapter->pm_wakeup_card_req = false; adapter->pm_wakeup_fw_try = false; diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/mwifiex/sta_tx.c index 877ad06..f6683ea 100644 --- a/drivers/net/wireless/mwifiex/sta_tx.c +++ b/drivers/net/wireless/mwifiex/sta_tx.c @@ -153,6 +153,10 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) if (adapter->data_sent) return -1; + if (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv)) + return -1; + skb = dev_alloc_skb(data_len); if (!skb) return -1; @@ -174,7 +178,7 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) local_tx_pd->bss_type = priv->bss_type; if (adapter->iface_type == MWIFIEX_USB) { - ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_DATA, + ret = adapter->if_ops.host_to_card(adapter, priv->usb_port, skb, NULL); } else { skb_push(skb, INTF_HEADER_LEN); diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c index 90f4915..bf6182b 100644 --- a/drivers/net/wireless/mwifiex/txrx.c +++ b/drivers/net/wireless/mwifiex/txrx.c @@ -116,7 +116,7 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, local_tx_pd = (struct txpd *)(head_ptr + hroom); if (adapter->iface_type == MWIFIEX_USB) { ret = adapter->if_ops.host_to_card(adapter, - MWIFIEX_USB_EP_DATA, + priv->usb_port, skb, NULL); } else { ret = adapter->if_ops.host_to_card(adapter, @@ -189,7 +189,7 @@ static int mwifiex_host_to_card(struct mwifiex_adapter *adapter, if (adapter->iface_type == MWIFIEX_USB) { ret = adapter->if_ops.host_to_card(adapter, - MWIFIEX_USB_EP_DATA, + priv->usb_port, skb, NULL); } else { ret = adapter->if_ops.host_to_card(adapter, diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c index 46c972a..078834c 100644 --- a/drivers/net/wireless/mwifiex/uap_event.c +++ b/drivers/net/wireless/mwifiex/uap_event.c @@ -269,7 +269,9 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv) adapter->tx_lock_flag = false; if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) { if (mwifiex_check_last_packet_indication(priv)) { - if (adapter->data_sent) { + if (adapter->data_sent || + (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv))) { adapter->ps_state = PS_STATE_AWAKE; adapter->pm_wakeup_card_req = false; adapter->pm_wakeup_fw_try = false; diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c index 78ad857..9f5356e 100644 --- a/drivers/net/wireless/mwifiex/usb.c +++ b/drivers/net/wireless/mwifiex/usb.c @@ -282,6 +282,7 @@ static void mwifiex_usb_tx_complete(struct urb *urb) port = &card->port[i]; if (context->ep == port->tx_data_ep) { atomic_dec(&port->tx_data_urb_pending); + port->block_status = false; break; } } @@ -823,6 +824,31 @@ static void mwifiex_usb_port_resync(struct mwifiex_adapter *adapter) } } +static bool mwifiex_usb_is_port_ready(struct mwifiex_private *priv) +{ + struct usb_card_rec *card = priv->adapter->card; + int idx; + + for (idx = 0; idx < MWIFIEX_TX_DATA_PORT; idx++) { + if (priv->usb_port == card->port[idx].tx_data_ep) + return !card->port[idx].block_status; + } + + return false; +} + +static inline u8 mwifiex_usb_data_sent(struct mwifiex_adapter *adapter) +{ + struct usb_card_rec *card = adapter->card; + int i; + + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) + if (!card->port[i].block_status) + return false; + + return true; +} + /* This function write a command/data packet to card. */ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, struct sk_buff *skb, @@ -833,7 +859,7 @@ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, struct usb_tx_data_port *port = NULL; u8 *data = (u8 *)skb->data; struct urb *tx_urb; - int idx; + int idx, ret; if (adapter->is_suspended) { mwifiex_dbg(adapter, ERROR, @@ -856,8 +882,9 @@ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, port = &card->port[idx]; if (atomic_read(&port->tx_data_urb_pending) >= MWIFIEX_TX_DATA_URB) { - adapter->data_sent = true; - return -EBUSY; + port->block_status = true; + ret = -EBUSY; + goto done; } if (port->tx_data_ix >= MWIFIEX_TX_DATA_URB) port->tx_data_ix = 0; @@ -895,7 +922,7 @@ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, atomic_dec(&card->tx_cmd_urb_pending); } else { atomic_dec(&port->tx_data_urb_pending); - adapter->data_sent = false; + port->block_status = false; if (port->tx_data_ix) port->tx_data_ix--; else @@ -907,12 +934,19 @@ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, if (ep != card->tx_cmd_ep && atomic_read(&port->tx_data_urb_pending) == MWIFIEX_TX_DATA_URB) { - adapter->data_sent = true; - return -ENOSR; + port->block_status = true; + ret = -ENOSR; + goto done; } } return -EINPROGRESS; + +done: + if (ep != card->tx_cmd_ep) + adapter->data_sent = mwifiex_usb_data_sent(adapter); + + return ret; } /* This function register usb device and initialize parameter. */ @@ -1189,6 +1223,7 @@ static struct mwifiex_if_ops usb_ops = { .host_to_card = mwifiex_usb_host_to_card, .submit_rem_rx_urbs = mwifiex_usb_submit_rem_rx_urbs, .multi_port_resync = mwifiex_usb_port_resync, + .is_port_ready = mwifiex_usb_is_port_ready, }; /* This function initializes the USB driver module. diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 4f303f3..7bbdfe4 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -452,7 +452,21 @@ mwifiex_wmm_init(struct mwifiex_adapter *adapter) int mwifiex_bypass_txlist_empty(struct mwifiex_adapter *adapter) { - return atomic_read(&adapter->bypass_tx_pending) ? false : true; + struct mwifiex_private *priv; + int i; + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (!priv) + continue; + if (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv)) + continue; + if (!skb_queue_empty(&priv->bypass_txq)) + return false; + } + + return true; } /* @@ -466,9 +480,14 @@ mwifiex_wmm_lists_empty(struct mwifiex_adapter *adapter) for (i = 0; i < adapter->priv_num; ++i) { priv = adapter->priv[i]; - if (priv && !priv->port_open) + if (!priv) + continue; + if (!priv->port_open) + continue; + if (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv)) continue; - if (priv && atomic_read(&priv->wmm.tx_pkts_queued)) + if (atomic_read(&priv->wmm.tx_pkts_queued)) return false; } @@ -1091,6 +1110,10 @@ mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter, (atomic_read(&priv_tmp->wmm.tx_pkts_queued) == 0)) continue; + if (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv_tmp)) + continue; + /* iterate over the WMM queues of the BSS */ hqp = &priv_tmp->wmm.highest_queued_prio; for (i = atomic_read(hqp); i >= LOW_PRIO_TID; --i) { @@ -1326,7 +1349,7 @@ mwifiex_send_processed_packet(struct mwifiex_private *priv, spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); if (adapter->iface_type == MWIFIEX_USB) { - ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_DATA, + ret = adapter->if_ops.host_to_card(adapter, priv->usb_port, skb, NULL); } else { tx_param.next_pkt_len = @@ -1467,6 +1490,13 @@ void mwifiex_process_bypass_tx(struct mwifiex_adapter *adapter) for (i = 0; i < adapter->priv_num; ++i) { priv = adapter->priv[i]; + if (!priv) + continue; + + if (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv)) + continue; + if (skb_queue_empty(&priv->bypass_txq)) continue; -- cgit v0.10.2 From 2b5d348ee786dd875b3aa8536a97ff6f6421d018 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Fri, 18 Sep 2015 22:08:04 +0200 Subject: brcmfmac: Fix exception handling. In some exception situations the ifp->vif was not properly assigned which could result in crash. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 600098d..da24bf0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -6332,6 +6332,7 @@ wiphy_unreg_out: priv_out: wl_deinit_priv(cfg); brcmf_free_vif(vif); + ifp->vif = NULL; wiphy_out: brcmf_free_wiphy(wiphy); return NULL; -- cgit v0.10.2 From e3c92cb2eea395c9fbec7ceb678626565cbac0fe Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Fri, 18 Sep 2015 22:08:05 +0200 Subject: brcmfmac: Add support for the BCM4350 PCIE device. This patch adds support fo the BRCM4350 2x2 11ac PCIE device. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index e3b8e23..ff0c7c6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -647,6 +647,7 @@ static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci) return 0x198000; case BRCM_CC_4335_CHIP_ID: case BRCM_CC_4339_CHIP_ID: + case BRCM_CC_4350_CHIP_ID: case BRCM_CC_4354_CHIP_ID: case BRCM_CC_4356_CHIP_ID: case BRCM_CC_43567_CHIP_ID: diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index 255d49a..451022e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -47,6 +47,8 @@ enum brcmf_pcie_state { #define BRCMF_PCIE_43602_FW_NAME "brcm/brcmfmac43602-pcie.bin" #define BRCMF_PCIE_43602_NVRAM_NAME "brcm/brcmfmac43602-pcie.txt" +#define BRCMF_PCIE_4350_FW_NAME "brcm/brcmfmac4350-pcie.bin" +#define BRCMF_PCIE_4350_NVRAM_NAME "brcm/brcmfmac4350-pcie.txt" #define BRCMF_PCIE_4356_FW_NAME "brcm/brcmfmac4356-pcie.bin" #define BRCMF_PCIE_4356_NVRAM_NAME "brcm/brcmfmac4356-pcie.txt" #define BRCMF_PCIE_43570_FW_NAME "brcm/brcmfmac43570-pcie.bin" @@ -194,6 +196,8 @@ enum brcmf_pcie_state { MODULE_FIRMWARE(BRCMF_PCIE_43602_FW_NAME); MODULE_FIRMWARE(BRCMF_PCIE_43602_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4350_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4350_NVRAM_NAME); MODULE_FIRMWARE(BRCMF_PCIE_4356_FW_NAME); MODULE_FIRMWARE(BRCMF_PCIE_4356_NVRAM_NAME); MODULE_FIRMWARE(BRCMF_PCIE_43570_FW_NAME); @@ -1418,6 +1422,10 @@ static int brcmf_pcie_get_fwnames(struct brcmf_pciedev_info *devinfo) fw_name = BRCMF_PCIE_43602_FW_NAME; nvram_name = BRCMF_PCIE_43602_NVRAM_NAME; break; + case BRCM_CC_4350_CHIP_ID: + fw_name = BRCMF_PCIE_4350_FW_NAME; + nvram_name = BRCMF_PCIE_4350_NVRAM_NAME; + break; case BRCM_CC_4356_CHIP_ID: fw_name = BRCMF_PCIE_4356_FW_NAME; nvram_name = BRCMF_PCIE_4356_NVRAM_NAME; @@ -1956,6 +1964,7 @@ cleanup: PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 } static struct pci_device_id brcmf_pcie_devid_table[] = { + BRCMF_PCIE_DEVICE(BRCM_PCIE_4350_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID), diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h index 7a6daa3..8278376 100644 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h @@ -39,6 +39,7 @@ #define BRCM_CC_4339_CHIP_ID 0x4339 #define BRCM_CC_43430_CHIP_ID 43430 #define BRCM_CC_4345_CHIP_ID 0x4345 +#define BRCM_CC_4350_CHIP_ID 0x4350 #define BRCM_CC_4354_CHIP_ID 0x4354 #define BRCM_CC_4356_CHIP_ID 0x4356 #define BRCM_CC_43566_CHIP_ID 43566 @@ -56,6 +57,7 @@ #define BRCM_USB_BCMFW_DEVICE_ID 0x0bdc /* PCIE Device IDs */ +#define BRCM_PCIE_4350_DEVICE_ID 0x43a3 #define BRCM_PCIE_4354_DEVICE_ID 0x43df #define BRCM_PCIE_4356_DEVICE_ID 0x43ec #define BRCM_PCIE_43567_DEVICE_ID 0x43d3 -- cgit v0.10.2 From 60dc35ef9ffdf8810b98cc09abb02378023818c2 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Fri, 18 Sep 2015 22:08:06 +0200 Subject: brcmfmac: Fix set and get tx-power functions. Implementation of tx-power (get and set) related functions are still assuming mW interface. This is wrong as functions use dbm (or mbm) nowadays. As a result a tx power configuration could result in wrong power configuration. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index da24bf0..0a97e61 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -236,89 +236,6 @@ static int brcmf_roamoff; module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR); MODULE_PARM_DESC(roamoff, "do not use internal roaming engine"); -/* Quarter dBm units to mW - * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153 - * Table is offset so the last entry is largest mW value that fits in - * a u16. - */ - -#define QDBM_OFFSET 153 /* Offset for first entry */ -#define QDBM_TABLE_LEN 40 /* Table size */ - -/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET. - * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2 - */ -#define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */ - -/* Largest mW value that will round down to the last table entry, - * QDBM_OFFSET + QDBM_TABLE_LEN-1. - * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) + - * mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2. - */ -#define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */ - -static const u16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = { -/* qdBm: +0 +1 +2 +3 +4 +5 +6 +7 */ -/* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000, -/* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849, -/* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119, -/* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811, -/* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 63096 -}; - -static u16 brcmf_qdbm_to_mw(u8 qdbm) -{ - uint factor = 1; - int idx = qdbm - QDBM_OFFSET; - - if (idx >= QDBM_TABLE_LEN) - /* clamp to max u16 mW value */ - return 0xFFFF; - - /* scale the qdBm index up to the range of the table 0-40 - * where an offset of 40 qdBm equals a factor of 10 mW. - */ - while (idx < 0) { - idx += 40; - factor *= 10; - } - - /* return the mW value scaled down to the correct factor of 10, - * adding in factor/2 to get proper rounding. - */ - return (nqdBm_to_mW_map[idx] + factor / 2) / factor; -} - -static u8 brcmf_mw_to_qdbm(u16 mw) -{ - u8 qdbm; - int offset; - uint mw_uint = mw; - uint boundary; - - /* handle boundary case */ - if (mw_uint <= 1) - return 0; - - offset = QDBM_OFFSET; - - /* move mw into the range of the table */ - while (mw_uint < QDBM_TABLE_LOW_BOUND) { - mw_uint *= 10; - offset -= 40; - } - - for (qdbm = 0; qdbm < QDBM_TABLE_LEN - 1; qdbm++) { - boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm + 1] - - nqdBm_to_mW_map[qdbm]) / 2; - if (mw_uint < boundary) - break; - } - - qdbm += (u8) offset; - - return qdbm; -} static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, struct cfg80211_chan_def *ch) @@ -2017,16 +1934,14 @@ static s32 brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, enum nl80211_tx_power_setting type, s32 mbm) { - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct net_device *ndev = cfg_to_ndev(cfg); struct brcmf_if *ifp = netdev_priv(ndev); - u16 txpwrmw; - s32 err = 0; - s32 disable = 0; - s32 dbm = MBM_TO_DBM(mbm); + s32 err; + s32 disable; + u32 qdbm = 127; - brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(TRACE, "Enter %d %d\n", type, mbm); if (!check_vif_up(ifp->vif)) return -EIO; @@ -2035,12 +1950,20 @@ brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, break; case NL80211_TX_POWER_LIMITED: case NL80211_TX_POWER_FIXED: - if (dbm < 0) { + if (mbm < 0) { brcmf_err("TX_POWER_FIXED - dbm is negative\n"); err = -EINVAL; goto done; } + qdbm = MBM_TO_DBM(4 * mbm); + if (qdbm > 127) + qdbm = 127; + qdbm |= WL_TXPWR_OVERRIDE; break; + default: + brcmf_err("Unsupported type %d\n", type); + err = -EINVAL; + goto done; } /* Make sure radio is off or on as far as software is concerned */ disable = WL_RADIO_SW_DISABLE << 16; @@ -2048,52 +1971,44 @@ brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, if (err) brcmf_err("WLC_SET_RADIO error (%d)\n", err); - if (dbm > 0xffff) - txpwrmw = 0xffff; - else - txpwrmw = (u16) dbm; - err = brcmf_fil_iovar_int_set(ifp, "qtxpower", - (s32)brcmf_mw_to_qdbm(txpwrmw)); + err = brcmf_fil_iovar_int_set(ifp, "qtxpower", qdbm); if (err) brcmf_err("qtxpower error (%d)\n", err); - cfg->conf->tx_power = dbm; done: - brcmf_dbg(TRACE, "Exit\n"); + brcmf_dbg(TRACE, "Exit %d (qdbm)\n", qdbm & ~WL_TXPWR_OVERRIDE); return err; } -static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, - struct wireless_dev *wdev, - s32 *dbm) +static s32 +brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, + s32 *dbm) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); - s32 txpwrdbm; - u8 result; - s32 err = 0; + struct net_device *ndev = cfg_to_ndev(cfg); + struct brcmf_if *ifp = netdev_priv(ndev); + s32 qdbm = 0; + s32 err; brcmf_dbg(TRACE, "Enter\n"); if (!check_vif_up(ifp->vif)) return -EIO; - err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &txpwrdbm); + err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &qdbm); if (err) { brcmf_err("error (%d)\n", err); goto done; } - - result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE); - *dbm = (s32) brcmf_qdbm_to_mw(result); + *dbm = (qdbm & ~WL_TXPWR_OVERRIDE) / 4; done: - brcmf_dbg(TRACE, "Exit\n"); + brcmf_dbg(TRACE, "Exit (0x%x %d)\n", qdbm, *dbm); return err; } static s32 brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev, - u8 key_idx, bool unicast, bool multicast) + u8 key_idx, bool unicast, bool multicast) { struct brcmf_if *ifp = netdev_priv(ndev); u32 index; -- cgit v0.10.2 From 6ea696eb4e2646e8f2440e2090f3d165a21ff02f Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Fri, 18 Sep 2015 22:08:07 +0200 Subject: brcmfmac: Only assign primary netdev to if2bss array. The if2bss allows for translation of ifidx to bssidx which has a 1:n relation. Therefor only the first (primary) netdev should be assigned in this array. This fixes the p2pon=1 module param usage. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index 17658b5..571c305 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -53,6 +53,8 @@ MODULE_LICENSE("Dual BSD/GPL"); #define BRCMF_RXREORDER_EXPIDX_VALID 0x08 #define BRCMF_RXREORDER_NEW_HOLE 0x10 +#define BRCMF_BSSIDX_INVALID -1 + /* Error bits */ int brcmf_msg_level; module_param_named(debug, brcmf_msg_level, int, S_IRUSR | S_IWUSR); @@ -837,7 +839,8 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, ifp = netdev_priv(ndev); ifp->ndev = ndev; /* store mapping ifidx to bssidx */ - drvr->if2bss[ifidx] = bssidx; + if (drvr->if2bss[ifidx] == BRCMF_BSSIDX_INVALID) + drvr->if2bss[ifidx] = bssidx; } ifp->drvr = drvr; @@ -862,15 +865,15 @@ static void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx) struct brcmf_if *ifp; ifp = drvr->iflist[bssidx]; - drvr->if2bss[ifp->ifidx] = -1; drvr->iflist[bssidx] = NULL; if (!ifp) { brcmf_err("Null interface, idx=%d\n", bssidx); return; } brcmf_dbg(TRACE, "Enter, idx=%d, ifidx=%d\n", bssidx, ifp->ifidx); + if (drvr->if2bss[ifp->ifidx] == bssidx) + drvr->if2bss[ifp->ifidx] = BRCMF_BSSIDX_INVALID; if (ifp->ndev) { - drvr->if2bss[ifp->ifidx] = -1; if (bssidx == 0) { if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) { rtnl_lock(); @@ -926,6 +929,7 @@ int brcmf_attach(struct device *dev) { struct brcmf_pub *drvr = NULL; int ret = 0; + int i; brcmf_dbg(TRACE, "Enter\n"); @@ -934,7 +938,9 @@ int brcmf_attach(struct device *dev) if (!drvr) return -ENOMEM; - memset(drvr->if2bss, 0xFF, sizeof(drvr->if2bss)); + for (i = 0; i < ARRAY_SIZE(drvr->if2bss); i++) + drvr->if2bss[i] = BRCMF_BSSIDX_INVALID; + mutex_init(&drvr->proto_block); /* Link to bus module */ -- cgit v0.10.2 From ae7c03f68ee24e51f3ded9265715405df72812ba Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Fri, 18 Sep 2015 22:08:08 +0200 Subject: brcmfmac: Inform p2p module about p2pon through API When the p2pon module param is used then p2p attach will initialize p2p device iface in the firmware, but it is doing that by checking data. It is cleaner to pass the p2pon information to p2p by API. This information is also needed for other patch. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 0a97e61..3e1c8fc 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -6127,7 +6127,8 @@ static void brcmf_free_wiphy(struct wiphy *wiphy) } struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, - struct device *busdev) + struct device *busdev, + bool p2pdev_forced) { struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev; struct brcmf_cfg80211_info *cfg; @@ -6219,7 +6220,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; } - err = brcmf_p2p_attach(cfg); + err = brcmf_p2p_attach(cfg, p2pdev_forced); if (err) { brcmf_err("P2P initilisation failed (%d)\n", err); goto wiphy_unreg_out; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h index d9e6d01..3f5e550 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h @@ -469,7 +469,8 @@ brcmf_cfg80211_connect_info *cfg_to_conn(struct brcmf_cfg80211_info *cfg) } struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, - struct device *busdev); + struct device *busdev, + bool p2pdev_forced); void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg); s32 brcmf_cfg80211_up(struct net_device *ndev); s32 brcmf_cfg80211_down(struct net_device *ndev); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index 571c305..3b169f5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -1044,7 +1044,8 @@ int brcmf_bus_start(struct device *dev) brcmf_fws_add_interface(ifp); - drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev); + drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev, + brcmf_p2p_enable); if (drvr->config == NULL) { ret = -ENOMEM; goto fail; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index 6a70c06..1e09cd2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -2336,7 +2336,7 @@ void brcmf_p2p_stop_device(struct wiphy *wiphy, struct wireless_dev *wdev) * * @cfg: driver private data for cfg80211 interface. */ -s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg) +s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced) { struct brcmf_if *pri_ifp; struct brcmf_if *p2p_ifp; @@ -2351,11 +2351,15 @@ s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg) drvr = cfg->pub; - pri_ifp = drvr->iflist[0]; - p2p_ifp = drvr->iflist[1]; - + pri_ifp = brcmf_get_ifp(drvr, 0); p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif = pri_ifp->vif; + if (p2pdev_forced) { + p2p_ifp = drvr->iflist[1]; + } else { + p2p_ifp = NULL; + p2p->p2pdev_dynamically = true; + } if (p2p_ifp) { p2p_vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_P2P_DEVICE, false); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.h b/drivers/net/wireless/brcm80211/brcmfmac/p2p.h index 872f382..d82287d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.h @@ -124,6 +124,7 @@ struct afx_hdl { * @wait_next_af: thread synchronizing struct. * @gon_req_action: about to send go negotiation requets frame. * @block_gon_req_tx: drop tx go negotiation requets frame. + * @p2pdev_dynamically: is p2p device if created by module param or supplicant. */ struct brcmf_p2p_info { struct brcmf_cfg80211_info *cfg; @@ -144,9 +145,10 @@ struct brcmf_p2p_info { struct completion wait_next_af; bool gon_req_action; bool block_gon_req_tx; + bool p2pdev_dynamically; }; -s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg); +s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced); void brcmf_p2p_detach(struct brcmf_p2p_info *p2p); struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, unsigned char name_assign_type, -- cgit v0.10.2 From 9f64df940cfe734156ec434807611e014278b359 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Fri, 18 Sep 2015 22:08:09 +0200 Subject: brcmfmac: Fix bug in flowring management. The hash index stored in the flowrings is of type u16 but gets stored in u8. This can result in incorrect indexing and possibly result in crashes. This patch fixes the type. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.h b/drivers/net/wireless/brcm80211/brcmfmac/flowring.h index 5551861..95fd1c9 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.h @@ -34,7 +34,7 @@ enum ring_status { }; struct brcmf_flowring_ring { - u8 hash_id; + u16 hash_id; bool blocked; enum ring_status status; struct sk_buff_head skblist; -- cgit v0.10.2 From 43e55a2a1cf05afcc4b9d4fc214ccb4a53447cb1 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Fri, 18 Sep 2015 22:08:10 +0200 Subject: brcmfmac: Make p2pon module param always available. p2pon module param is currently under define BRCMDBG. Though it is a needed option for older versions of the wpa_supplicant which do not support the P2P_DEVICE interface. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index 3b169f5..031283a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -62,10 +62,8 @@ MODULE_PARM_DESC(debug, "level of debug output"); /* P2P0 enable */ static int brcmf_p2p_enable; -#ifdef CONFIG_BRCMDBG module_param_named(p2pon, brcmf_p2p_enable, int, 0); -MODULE_PARM_DESC(p2pon, "enable p2p management functionality"); -#endif +MODULE_PARM_DESC(p2pon, "enable legacy p2p management functionality"); char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx) { -- cgit v0.10.2 From 178e9ef9b6c619b1b3de354f21692a0ad1071116 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Fri, 18 Sep 2015 22:08:11 +0200 Subject: brcmfmac: Workaround in change vif for wpa_supplicant support. Different wpa_supplicants have different behavior and expectations regarding the change_virtual_intf behavior. This patch implements a workaround for the different versions and possible brcmfmac configuration. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 3e1c8fc..891f4ed 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -777,6 +777,37 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, s32 err = 0; brcmf_dbg(TRACE, "Enter, idx=%d, type=%d\n", ifp->bssidx, type); + + /* WAR: There are a number of p2p interface related problems which + * need to be handled initially (before doing the validate). + * wpa_supplicant tends to do iface changes on p2p device/client/go + * which are not always possible/allowed. However we need to return + * OK otherwise the wpa_supplicant wont start. The situation differs + * on configuration and setup (p2pon=1 module param). The first check + * is to see if the request is a change to station for p2p iface. + */ + if ((type == NL80211_IFTYPE_STATION) && + ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) || + (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) || + (vif->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE))) { + brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n"); + /* Now depending on whether module param p2pon=1 was used the + * response needs to be either 0 or EOPNOTSUPP. The reason is + * that if p2pon=1 is used, but a newer supplicant is used then + * we should return an error, as this combination wont work. + * In other situations 0 is returned and supplicant will start + * normally. It will give a trace in cfg80211, but it is the + * only way to get it working. Unfortunately this will result + * in situation where we wont support new supplicant in + * combination with module param p2pon=1, but that is the way + * it is. If the user tries this then unloading of driver might + * fail/lock. + */ + if (cfg->p2p.p2pdev_dynamically) + return -EOPNOTSUPP; + else + return 0; + } err = brcmf_vif_change_validate(wiphy_to_cfg(wiphy), vif, type); if (err) { brcmf_err("iface validation failed: err=%d\n", err); @@ -792,18 +823,6 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, infra = 0; break; case NL80211_IFTYPE_STATION: - /* Ignore change for p2p IF. Unclear why supplicant does this */ - if ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) || - (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO)) { - brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n"); - /* WAR: It is unexpected to get a change of VIF for P2P - * IF, but it happens. The request can not be handled - * but returning EPERM causes a crash. Returning 0 - * without setting ieee80211_ptr->iftype causes trace - * (WARN_ON) but it works with wpa_supplicant - */ - return 0; - } infra = 1; break; case NL80211_IFTYPE_AP: -- cgit v0.10.2 From 9831bcb987df4aff6a1d85975f1dda4c654f298d Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Fri, 18 Sep 2015 22:08:12 +0200 Subject: brcmfmac: Deleting of p2p device is leaking memory. When a p2p device gets deleted, the memory for the vif is not being released. This is solved by reorganizing the cleanup path and properly freeing the memory. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index 031283a..8c2a280 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -887,6 +887,16 @@ static void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx) cancel_work_sync(&ifp->multicast_work); } brcmf_net_detach(ifp->ndev); + } else { + /* Only p2p device interfaces which get dynamically created + * end up here. In this case the p2p module should be informed + * about the removal of the interface within the firmware. If + * not then p2p commands towards the firmware will cause some + * serious troublesome side effects. The p2p module will clean + * up the ifp if needed. + */ + brcmf_p2p_ifp_removed(ifp); + kfree(ifp); } } @@ -894,7 +904,8 @@ void brcmf_remove_interface(struct brcmf_if *ifp) { if (!ifp || WARN_ON(ifp->drvr->iflist[ifp->bssidx] != ifp)) return; - + brcmf_dbg(TRACE, "Enter, bssidx=%d, ifidx=%d\n", ifp->bssidx, + ifp->ifidx); brcmf_fws_del_interface(ifp); brcmf_del_if(ifp->drvr, ifp->bssidx); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index 1e09cd2..83027dc 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -2131,20 +2131,6 @@ fail: } /** - * brcmf_p2p_delete_p2pdev() - delete P2P_DEVICE virtual interface. - * - * @vif: virtual interface object to delete. - */ -static void brcmf_p2p_delete_p2pdev(struct brcmf_p2p_info *p2p, - struct brcmf_cfg80211_vif *vif) -{ - cfg80211_unregister_wdev(&vif->wdev); - p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL; - brcmf_remove_interface(vif->ifp); - brcmf_free_vif(vif); -} - -/** * brcmf_p2p_add_vif() - create a new P2P virtual interface. * * @wiphy: wiphy device of new interface. @@ -2267,9 +2253,11 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) break; case NL80211_IFTYPE_P2P_DEVICE: + if (!p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif) + return 0; brcmf_p2p_cancel_remain_on_channel(vif->ifp); brcmf_p2p_deinit_discovery(p2p); - brcmf_p2p_delete_p2pdev(p2p, vif); + brcmf_remove_interface(vif->ifp); return 0; default: return -ENOTSUPP; @@ -2301,6 +2289,21 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) return err; } +void brcmf_p2p_ifp_removed(struct brcmf_if *ifp) +{ + struct brcmf_cfg80211_info *cfg; + struct brcmf_cfg80211_vif *vif; + + brcmf_dbg(INFO, "P2P: device interface removed\n"); + vif = ifp->vif; + cfg = wdev_to_cfg(&vif->wdev); + cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL; + rtnl_lock(); + cfg80211_unregister_wdev(&vif->wdev); + rtnl_unlock(); + brcmf_free_vif(vif); +} + int brcmf_p2p_start_device(struct wiphy *wiphy, struct wireless_dev *wdev) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); @@ -2425,10 +2428,7 @@ void brcmf_p2p_detach(struct brcmf_p2p_info *p2p) if (vif != NULL) { brcmf_p2p_cancel_remain_on_channel(vif->ifp); brcmf_p2p_deinit_discovery(p2p); - /* remove discovery interface */ - rtnl_lock(); - brcmf_p2p_delete_p2pdev(p2p, vif); - rtnl_unlock(); + brcmf_remove_interface(vif->ifp); } /* just set it all to zero */ memset(p2p, 0, sizeof(*p2p)); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.h b/drivers/net/wireless/brcm80211/brcmfmac/p2p.h index d82287d..5d49059 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.h @@ -157,6 +157,7 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev); int brcmf_p2p_ifchange(struct brcmf_cfg80211_info *cfg, enum brcmf_fil_p2p_if_types if_type); +void brcmf_p2p_ifp_removed(struct brcmf_if *ifp); int brcmf_p2p_start_device(struct wiphy *wiphy, struct wireless_dev *wdev); void brcmf_p2p_stop_device(struct wiphy *wiphy, struct wireless_dev *wdev); int brcmf_p2p_scan_prep(struct wiphy *wiphy, -- cgit v0.10.2 From d1bb34c128f59c30b75b96ef60f5f00cdbe9ca0e Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Fri, 18 Sep 2015 22:08:13 +0200 Subject: brcmfmac: Only handle p2p_stop_device if vif is valid In some situations it is possible that vif has been removed while cfg80211 invokes the p2p_stop_device handler. This will result in crash. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index 83027dc..76e4771 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -2327,11 +2327,17 @@ void brcmf_p2p_stop_device(struct wiphy *wiphy, struct wireless_dev *wdev) struct brcmf_cfg80211_vif *vif; vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); - mutex_lock(&cfg->usr_sync); - (void)brcmf_p2p_deinit_discovery(p2p); - brcmf_abort_scanning(cfg); - clear_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state); - mutex_unlock(&cfg->usr_sync); + /* This call can be result of the unregister_wdev call. In that case + * we dont want to do anything anymore. Just return. The config vif + * will have been cleared at this point. + */ + if (p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif == vif) { + mutex_lock(&cfg->usr_sync); + (void)brcmf_p2p_deinit_discovery(p2p); + brcmf_abort_scanning(cfg); + clear_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state); + mutex_unlock(&cfg->usr_sync); + } } /** -- cgit v0.10.2 From 35a3cbcc49b5422fbfe7d655952888a3d29b45ed Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Fri, 18 Sep 2015 22:08:14 +0200 Subject: brcmfmac: Fix p2p bug for older firmwares. Some devices with older firmwares are reporting new p2p device interface with the wrong type. Accept this type to get p2p working for these devices. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c index 3330f30..dc34d3a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c @@ -186,11 +186,13 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, ifevent->action, ifevent->ifidx, ifevent->bssidx, ifevent->flags, ifevent->role); - /* The P2P Device interface event must not be ignored - * contrary to what firmware tells us. + /* The P2P Device interface event must not be ignored contrary to what + * firmware tells us. Older firmware uses p2p noif, with sta role. + * This should be accepted. */ - is_p2pdev = (ifevent->flags & BRCMF_E_IF_FLAG_NOIF) && - ifevent->role == BRCMF_E_IF_ROLE_P2P_CLIENT; + is_p2pdev = ((ifevent->flags & BRCMF_E_IF_FLAG_NOIF) && + (ifevent->role == BRCMF_E_IF_ROLE_P2P_CLIENT || + ifevent->role == BRCMF_E_IF_ROLE_STA)); if (!is_p2pdev && (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) { brcmf_dbg(EVENT, "event can be ignored\n"); return; -- cgit v0.10.2 From 8268c2011d255b547a9093977e728af5cf16d69e Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Fri, 18 Sep 2015 22:08:15 +0200 Subject: brcmfmac: Add module parameter to disable features. For debugging purpose it is very handy to be able to disable features. It has happened a few times that new features turned out not always being properly detected for all devices/firmwares. Making it possible to disable the feature with a module parameter will make testing/debugging easier. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c index 7c2135f..44bb306 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c @@ -15,6 +15,7 @@ */ #include +#include #include #include "core.h" @@ -23,6 +24,12 @@ #include "fwil.h" #include "feature.h" + +/* Module param feature_disable (global for all devices) */ +static int brcmf_feature_disable; +module_param_named(feature_disable, brcmf_feature_disable, int, 0); +MODULE_PARM_DESC(feature_disable, "Disable features"); + /* * expand feature list to array of feature strings. */ @@ -131,6 +138,12 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_P2P, "p2p"); + if (brcmf_feature_disable) { + brcmf_dbg(INFO, "Features: 0x%02x, disable: 0x%02x\n", + ifp->drvr->feat_flags, brcmf_feature_disable); + ifp->drvr->feat_flags &= ~brcmf_feature_disable; + } + /* set chip related quirks */ switch (drvr->bus_if->chip) { case BRCM_CC_43236_CHIP_ID: -- cgit v0.10.2 From a2044d91d9d25a0e9e2abb43ca90cceee032f6ee Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Fri, 18 Sep 2015 22:08:16 +0200 Subject: brcmfmac: Fix race condition bug when deleting p2p interface. When p2p device interface gets deleted by deinitialising discovery it will result in an event which removes the interface, but that is also done by delete p2p interface code. This results in race condition which sometimes results in lockup/crash. With this patch the delete device interface will wait for the event (with timeout) removing the possible race condition. Also on the stop device call from cfg80211 the deinitialisation of the discovery device should be avoided as it can result in a similar situation. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index 76e4771..fda726c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -2241,6 +2241,7 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) brcmf_dbg(TRACE, "delete P2P vif\n"); vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + brcmf_cfg80211_arm_vif_event(cfg, vif); switch (vif->wdev.iftype) { case NL80211_IFTYPE_P2P_CLIENT: if (test_bit(BRCMF_VIF_STATUS_DISCONNECTING, &vif->sme_state)) @@ -2257,8 +2258,6 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) return 0; brcmf_p2p_cancel_remain_on_channel(vif->ifp); brcmf_p2p_deinit_discovery(p2p); - brcmf_remove_interface(vif->ifp); - return 0; default: return -ENOTSUPP; } @@ -2270,10 +2269,11 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) wait_for_completion_timeout(&cfg->vif_disabled, msecs_to_jiffies(500)); - brcmf_vif_clear_mgmt_ies(vif); - - brcmf_cfg80211_arm_vif_event(cfg, vif); - err = brcmf_p2p_release_p2p_if(vif); + err = 0; + if (vif->wdev.iftype != NL80211_IFTYPE_P2P_DEVICE) { + brcmf_vif_clear_mgmt_ies(vif); + err = brcmf_p2p_release_p2p_if(vif); + } if (!err) { /* wait for firmware event */ err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_DEL, @@ -2283,8 +2283,12 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) else err = 0; } + if (err) + brcmf_remove_interface(vif->ifp); + brcmf_cfg80211_arm_vif_event(cfg, NULL); - p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = NULL; + if (vif->wdev.iftype != NL80211_IFTYPE_P2P_DEVICE) + p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = NULL; return err; } @@ -2333,7 +2337,9 @@ void brcmf_p2p_stop_device(struct wiphy *wiphy, struct wireless_dev *wdev) */ if (p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif == vif) { mutex_lock(&cfg->usr_sync); - (void)brcmf_p2p_deinit_discovery(p2p); + /* Set the discovery state to SCAN */ + (void)brcmf_p2p_set_discover_state(vif->ifp, + WL_P2P_DISC_ST_SCAN, 0, 0); brcmf_abort_scanning(cfg); clear_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state); mutex_unlock(&cfg->usr_sync); -- cgit v0.10.2 From 55acca90da52b85299c033354e51ddaa7b73e019 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Fri, 18 Sep 2015 22:08:17 +0200 Subject: brcmfmac: Add support for the BCM4365 and BCM4366 PCIE devices. This patch adds support for the BCM4365 and BCM4366 11ac Wave2 PCIE devices. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index ff0c7c6..ffc3ace 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -208,6 +208,7 @@ struct sbsocramregs { }; #define SOCRAMREGOFFS(_f) offsetof(struct sbsocramregs, _f) +#define SYSMEMREGOFFS(_f) offsetof(struct sbsocramregs, _f) #define ARMCR4_CAP (0x04) #define ARMCR4_BANKIDX (0x40) @@ -516,6 +517,9 @@ static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) case BCMA_CORE_ARM_CR4: cpu_found = true; break; + case BCMA_CORE_ARM_CA7: + cpu_found = true; + break; default: break; } @@ -614,6 +618,29 @@ static void brcmf_chip_socram_ramsize(struct brcmf_core_priv *sr, u32 *ramsize, } } +/** Return the SYS MEM size */ +static u32 brcmf_chip_sysmem_ramsize(struct brcmf_core_priv *sysmem) +{ + u32 memsize = 0; + u32 coreinfo; + u32 idx; + u32 nb; + u32 banksize; + + if (!brcmf_chip_iscoreup(&sysmem->pub)) + brcmf_chip_resetcore(&sysmem->pub, 0, 0, 0); + + coreinfo = brcmf_chip_core_read32(sysmem, SYSMEMREGOFFS(coreinfo)); + nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; + + for (idx = 0; idx < nb; idx++) { + brcmf_chip_socram_banksize(sysmem, idx, &banksize); + memsize += banksize; + } + + return memsize; +} + /** Return the TCM-RAM size of the ARMCR4 core. */ static u32 brcmf_chip_tcm_ramsize(struct brcmf_core_priv *cr4) { @@ -656,6 +683,9 @@ static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci) case BRCM_CC_4358_CHIP_ID: case BRCM_CC_43602_CHIP_ID: return 0x180000; + case BRCM_CC_4365_CHIP_ID: + case BRCM_CC_4366_CHIP_ID: + return 0x200000; default: brcmf_err("unknown chip: %s\n", ci->pub.name); break; @@ -678,10 +708,28 @@ static int brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) return -EINVAL; } } else { - mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_INTERNAL_MEM); - mem_core = container_of(mem, struct brcmf_core_priv, pub); - brcmf_chip_socram_ramsize(mem_core, &ci->pub.ramsize, - &ci->pub.srsize); + mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_SYS_MEM); + if (mem) { + mem_core = container_of(mem, struct brcmf_core_priv, + pub); + ci->pub.ramsize = brcmf_chip_sysmem_ramsize(mem_core); + ci->pub.rambase = brcmf_chip_tcm_rambase(ci); + if (!ci->pub.rambase) { + brcmf_err("RAM base not provided with ARM CA7 core\n"); + return -EINVAL; + } + } else { + mem = brcmf_chip_get_core(&ci->pub, + BCMA_CORE_INTERNAL_MEM); + if (!mem) { + brcmf_err("No memory cores found\n"); + return -ENOMEM; + } + mem_core = container_of(mem, struct brcmf_core_priv, + pub); + brcmf_chip_socram_ramsize(mem_core, &ci->pub.ramsize, + &ci->pub.srsize); + } } brcmf_dbg(INFO, "RAM: base=0x%x size=%d (0x%x) sr=%d (0x%x)\n", ci->pub.rambase, ci->pub.ramsize, ci->pub.ramsize, @@ -924,7 +972,7 @@ static int brcmf_chip_recognition(struct brcmf_chip_priv *ci) static void brcmf_chip_disable_arm(struct brcmf_chip_priv *chip, u16 id) { struct brcmf_core *core; - struct brcmf_core_priv *cr4; + struct brcmf_core_priv *cpu; u32 val; @@ -937,10 +985,11 @@ static void brcmf_chip_disable_arm(struct brcmf_chip_priv *chip, u16 id) brcmf_chip_coredisable(core, 0, 0); break; case BCMA_CORE_ARM_CR4: - cr4 = container_of(core, struct brcmf_core_priv, pub); + case BCMA_CORE_ARM_CA7: + cpu = container_of(core, struct brcmf_core_priv, pub); /* clear all IOCTL bits except HALT bit */ - val = chip->ops->read32(chip->ctx, cr4->wrapbase + BCMA_IOCTL); + val = chip->ops->read32(chip->ctx, cpu->wrapbase + BCMA_IOCTL); val &= ARMCR4_BCMA_IOCTL_CPUHALT; brcmf_chip_resetcore(core, val, ARMCR4_BCMA_IOCTL_CPUHALT, ARMCR4_BCMA_IOCTL_CPUHALT); @@ -1162,6 +1211,33 @@ static bool brcmf_chip_cr4_set_active(struct brcmf_chip_priv *chip, u32 rstvec) return true; } +static inline void +brcmf_chip_ca7_set_passive(struct brcmf_chip_priv *chip) +{ + struct brcmf_core *core; + + brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CA7); + + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211); + brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET | + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN); +} + +static bool brcmf_chip_ca7_set_active(struct brcmf_chip_priv *chip, u32 rstvec) +{ + struct brcmf_core *core; + + chip->ops->activate(chip->ctx, &chip->pub, rstvec); + + /* restore ARM */ + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CA7); + brcmf_chip_resetcore(core, ARMCR4_BCMA_IOCTL_CPUHALT, 0, 0); + + return true; +} + void brcmf_chip_set_passive(struct brcmf_chip *pub) { struct brcmf_chip_priv *chip; @@ -1175,8 +1251,16 @@ void brcmf_chip_set_passive(struct brcmf_chip *pub) brcmf_chip_cr4_set_passive(chip); return; } - - brcmf_chip_cm3_set_passive(chip); + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CA7); + if (arm) { + brcmf_chip_ca7_set_passive(chip); + return; + } + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CM3); + if (arm) { + brcmf_chip_cm3_set_passive(chip); + return; + } } bool brcmf_chip_set_active(struct brcmf_chip *pub, u32 rstvec) @@ -1190,8 +1274,14 @@ bool brcmf_chip_set_active(struct brcmf_chip *pub, u32 rstvec) arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4); if (arm) return brcmf_chip_cr4_set_active(chip, rstvec); + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CA7); + if (arm) + return brcmf_chip_ca7_set_active(chip, rstvec); + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CM3); + if (arm) + return brcmf_chip_cm3_set_active(chip); - return brcmf_chip_cm3_set_active(chip); + return false; } bool brcmf_chip_sr_capable(struct brcmf_chip *pub) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index 451022e..30baf35 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -55,6 +55,10 @@ enum brcmf_pcie_state { #define BRCMF_PCIE_43570_NVRAM_NAME "brcm/brcmfmac43570-pcie.txt" #define BRCMF_PCIE_4358_FW_NAME "brcm/brcmfmac4358-pcie.bin" #define BRCMF_PCIE_4358_NVRAM_NAME "brcm/brcmfmac4358-pcie.txt" +#define BRCMF_PCIE_4365_FW_NAME "brcm/brcmfmac4365b-pcie.bin" +#define BRCMF_PCIE_4365_NVRAM_NAME "brcm/brcmfmac4365b-pcie.txt" +#define BRCMF_PCIE_4366_FW_NAME "brcm/brcmfmac4366b-pcie.bin" +#define BRCMF_PCIE_4366_NVRAM_NAME "brcm/brcmfmac4366b-pcie.txt" #define BRCMF_PCIE_FW_UP_TIMEOUT 2000 /* msec */ @@ -204,6 +208,10 @@ MODULE_FIRMWARE(BRCMF_PCIE_43570_FW_NAME); MODULE_FIRMWARE(BRCMF_PCIE_43570_NVRAM_NAME); MODULE_FIRMWARE(BRCMF_PCIE_4358_FW_NAME); MODULE_FIRMWARE(BRCMF_PCIE_4358_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4365_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4365_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4366_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4366_NVRAM_NAME); struct brcmf_pcie_console { @@ -1440,6 +1448,14 @@ static int brcmf_pcie_get_fwnames(struct brcmf_pciedev_info *devinfo) fw_name = BRCMF_PCIE_4358_FW_NAME; nvram_name = BRCMF_PCIE_4358_NVRAM_NAME; break; + case BRCM_CC_4365_CHIP_ID: + fw_name = BRCMF_PCIE_4365_FW_NAME; + nvram_name = BRCMF_PCIE_4365_NVRAM_NAME; + break; + case BRCM_CC_4366_CHIP_ID: + fw_name = BRCMF_PCIE_4366_FW_NAME; + nvram_name = BRCMF_PCIE_4366_NVRAM_NAME; + break; default: brcmf_err("Unsupported chip 0x%04x\n", devinfo->ci->chip); return -ENODEV; @@ -1973,6 +1989,12 @@ static struct pci_device_id brcmf_pcie_devid_table[] = { BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_2G_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_5G_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_RAW_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_2G_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_5G_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_2G_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_5G_DEVICE_ID), { /* end: all zeroes */ } }; diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h index 8278376..d823734 100644 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h @@ -48,6 +48,8 @@ #define BRCM_CC_43570_CHIP_ID 43570 #define BRCM_CC_4358_CHIP_ID 0x4358 #define BRCM_CC_43602_CHIP_ID 43602 +#define BRCM_CC_4365_CHIP_ID 0x4365 +#define BRCM_CC_4366_CHIP_ID 0x4366 /* USB Device IDs */ #define BRCM_USB_43143_DEVICE_ID 0xbd1e @@ -67,6 +69,13 @@ #define BRCM_PCIE_43602_2G_DEVICE_ID 0x43bb #define BRCM_PCIE_43602_5G_DEVICE_ID 0x43bc #define BRCM_PCIE_43602_RAW_DEVICE_ID 43602 +#define BRCM_PCIE_4365_DEVICE_ID 0x43ca +#define BRCM_PCIE_4365_2G_DEVICE_ID 0x43cb +#define BRCM_PCIE_4365_5G_DEVICE_ID 0x43cc +#define BRCM_PCIE_4366_DEVICE_ID 0x43c3 +#define BRCM_PCIE_4366_2G_DEVICE_ID 0x43c4 +#define BRCM_PCIE_4366_5G_DEVICE_ID 0x43c5 + /* brcmsmac IDs */ #define BCM4313_D11N2G_ID 0x4727 /* 4313 802.11n 2.4G device */ diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 2ff4a99..3feb1b2 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -151,6 +151,8 @@ struct bcma_host_ops { #define BCMA_CORE_PCIE2 0x83C /* PCI Express Gen2 */ #define BCMA_CORE_USB30_DEV 0x83D #define BCMA_CORE_ARM_CR4 0x83E +#define BCMA_CORE_ARM_CA7 0x847 +#define BCMA_CORE_SYS_MEM 0x849 #define BCMA_CORE_DEFAULT 0xFFF #define BCMA_MAX_NR_CORES 16 -- cgit v0.10.2 From cb8dc71f516a98908de7de9be381ef9b571742e2 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Fri, 18 Sep 2015 22:08:18 +0200 Subject: brcmfmac: Fix TDLS setup by properly handling p2p noif. There is a workaround needed for p2p device setup which breaks tdls functionality. This patch fixes that by properly signalling fweh that p2p device setup is ongoing. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c index dc34d3a..81542e0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c @@ -188,11 +188,13 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, /* The P2P Device interface event must not be ignored contrary to what * firmware tells us. Older firmware uses p2p noif, with sta role. - * This should be accepted. + * This should be accepted when p2pdev_setup is ongoing. TDLS setup will + * use the same ifevent and should be ignored. */ is_p2pdev = ((ifevent->flags & BRCMF_E_IF_FLAG_NOIF) && (ifevent->role == BRCMF_E_IF_ROLE_P2P_CLIENT || - ifevent->role == BRCMF_E_IF_ROLE_STA)); + ((ifevent->role == BRCMF_E_IF_ROLE_STA) && + (drvr->fweh.p2pdev_setup_ongoing)))); if (!is_p2pdev && (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) { brcmf_dbg(EVENT, "event can be ignored\n"); return; @@ -316,6 +318,17 @@ event_free: } /** + * brcmf_fweh_p2pdev_setup() - P2P device setup ongoing (or not). + * + * @ifp: ifp on which setup is taking place or finished. + * @ongoing: p2p device setup in progress (or not). + */ +void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing) +{ + ifp->drvr->fweh.p2pdev_setup_ongoing = ongoing; +} + +/** * brcmf_fweh_attach() - initialize firmware event handling. * * @drvr: driver information object. diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h index 1326898..d9a9428 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h @@ -230,12 +230,14 @@ typedef int (*brcmf_fweh_handler_t)(struct brcmf_if *ifp, /** * struct brcmf_fweh_info - firmware event handling information. * + * @p2pdev_setup_ongoing: P2P device creation in progress. * @event_work: event worker. * @evt_q_lock: lock for event queue protection. * @event_q: event queue. * @evt_handler: registered event handlers. */ struct brcmf_fweh_info { + bool p2pdev_setup_ongoing; struct work_struct event_work; spinlock_t evt_q_lock; struct list_head event_q; @@ -255,6 +257,7 @@ void brcmf_fweh_unregister(struct brcmf_pub *drvr, int brcmf_fweh_activate_events(struct brcmf_if *ifp); void brcmf_fweh_process_event(struct brcmf_pub *drvr, struct brcmf_event *event_packet); +void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing); static inline void brcmf_fweh_process_skb(struct brcmf_pub *drvr, struct sk_buff *skb) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index fda726c..37a8c352 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -2084,11 +2084,13 @@ static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p, brcmf_p2p_set_firmware(pri_ifp, p2p->dev_addr); brcmf_cfg80211_arm_vif_event(p2p->cfg, p2p_vif); + brcmf_fweh_p2pdev_setup(pri_ifp, true); /* Initialize P2P Discovery in the firmware */ err = brcmf_fil_iovar_int_set(pri_ifp, "p2p_disc", 1); if (err < 0) { brcmf_err("set p2p_disc error\n"); + brcmf_fweh_p2pdev_setup(pri_ifp, false); brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL); goto fail; } @@ -2097,6 +2099,7 @@ static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p, err = brcmf_cfg80211_wait_vif_event_timeout(p2p->cfg, BRCMF_E_IF_ADD, msecs_to_jiffies(1500)); brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL); + brcmf_fweh_p2pdev_setup(pri_ifp, false); if (!err) { brcmf_err("timeout occurred\n"); err = -EIO; @@ -2396,6 +2399,8 @@ s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced) memcpy(p2p_ifp->mac_addr, p2p->dev_addr, ETH_ALEN); brcmf_p2p_set_firmware(pri_ifp, p2p->dev_addr); + brcmf_fweh_p2pdev_setup(pri_ifp, true); + /* Initialize P2P Discovery in the firmware */ err = brcmf_fil_iovar_int_set(pri_ifp, "p2p_disc", 1); if (err < 0) { @@ -2422,8 +2427,9 @@ s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced) INIT_WORK(&p2p->afx_hdl.afx_work, brcmf_p2p_afx_handler); init_completion(&p2p->afx_hdl.act_frm_scan); init_completion(&p2p->wait_next_af); - } exit: + brcmf_fweh_p2pdev_setup(pri_ifp, false); + } return err; } -- cgit v0.10.2 From 29b93c0e3c3891e3ba6e38a71997bdad8a2aa457 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Fri, 18 Sep 2015 22:08:19 +0200 Subject: brcmfmac: Accept events when TDLS is used in combination with p2p. TDLS events are mapped back to primary interface but when p2p is in use then this fails because the check was incorrect by checking bsscfg number. Which can be different when a p2p device has been created. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c index 81542e0..383d6fa 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c @@ -300,8 +300,7 @@ static void brcmf_fweh_event_worker(struct work_struct *work) goto event_free; } - if ((event->code == BRCMF_E_TDLS_PEER_EVENT) && - (emsg.bsscfgidx == 1)) + if (event->code == BRCMF_E_TDLS_PEER_EVENT) ifp = drvr->iflist[0]; else ifp = drvr->iflist[emsg.bsscfgidx]; -- cgit v0.10.2 From a32be01772525eba2001be23a570d9e31c58273d Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 19 Sep 2015 12:47:20 +0200 Subject: brcmfmac: include linux/atomic.h brcmfmac uses atomic_or() and other atomic_* functions, but does not include linux/atomic.h. This file gets included by some other header file so this normally does not cause problems. Signed-off-by: Hauke Mehrtens Acked-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index e41bf44..7f574f2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -15,6 +15,7 @@ */ #include +#include #include #include #include -- cgit v0.10.2 From 69b5f4da26362912e7e56e48c8e1df7fde281e58 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 19 Sep 2015 14:43:50 +0200 Subject: bcma: add support for population subnodes also when build as module of_default_bus_match_table was not exported earlier, so it could only be accessed by code compiled into the kernel. A new function of_platform_default_populate() was added which uses of_default_bus_match_table and this function is also exported. This way it is possible to create a bus with the content of of_default_bus_match_table and we can remove the hacks from bcma. Signed-off-by: Hauke Mehrtens Signed-off-by: Kalle Valo diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index 24882c1..59d8d0d 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -436,13 +436,8 @@ int bcma_bus_register(struct bcma_bus *bus) } dev = bcma_bus_get_host_dev(bus); - /* TODO: remove check for IS_BUILTIN(CONFIG_BCMA) check when - * of_default_bus_match_table is exported or in some other way - * accessible. This is just a temporary workaround. - */ - if (IS_BUILTIN(CONFIG_BCMA) && dev) { - of_platform_populate(dev->of_node, of_default_bus_match_table, - NULL, dev); + if (dev) { + of_platform_default_populate(dev->of_node, NULL, dev); } /* Cores providing flash access go before SPROM init */ -- cgit v0.10.2 From 44f3fd2700eee30e03fe9da6258b73d53bc037c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Mon, 21 Sep 2015 09:47:18 +0200 Subject: ssb: unexport ssb_bus_pcibus_register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It isn't used anywhere out of ssb code and we don't (plan to) build pcihost_wrapper.c as a separated module. Signed-off-by: Rafał Miłecki Signed-off-by: Kalle Valo diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c index a48a743..8cf23a2 100644 --- a/drivers/ssb/main.c +++ b/drivers/ssb/main.c @@ -876,7 +876,6 @@ int ssb_bus_pcibus_register(struct ssb_bus *bus, struct pci_dev *host_pci) return err; } -EXPORT_SYMBOL(ssb_bus_pcibus_register); #endif /* CONFIG_SSB_PCIHOST */ #ifdef CONFIG_SSB_PCMCIAHOST -- cgit v0.10.2 From 7b1647bc1a44422ee9d67d4d2b5d3ab12d2c5bee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Mon, 21 Sep 2015 09:47:19 +0200 Subject: ssb: make ssb_sdio_switch_core static MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's used locally only. Signed-off-by: Rafał Miłecki Signed-off-by: Kalle Valo diff --git a/drivers/ssb/sdio.c b/drivers/ssb/sdio.c index b2d36f7..2278e43 100644 --- a/drivers/ssb/sdio.c +++ b/drivers/ssb/sdio.c @@ -200,7 +200,7 @@ out: } /* host must be already claimed */ -int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev) +static int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev) { u8 coreidx = dev->core_index; u32 sbaddr; diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h index eb507a5..1d2256e 100644 --- a/drivers/ssb/ssb_private.h +++ b/drivers/ssb/ssb_private.h @@ -132,7 +132,6 @@ extern int ssb_sdio_get_invariants(struct ssb_bus *bus, struct ssb_init_invariants *iv); extern u32 ssb_sdio_scan_read32(struct ssb_bus *bus, u16 offset); -extern int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev); extern int ssb_sdio_scan_switch_coreidx(struct ssb_bus *bus, u8 coreidx); extern int ssb_sdio_hardware_setup(struct ssb_bus *bus); extern void ssb_sdio_exit(struct ssb_bus *bus); @@ -144,11 +143,6 @@ static inline u32 ssb_sdio_scan_read32(struct ssb_bus *bus, u16 offset) { return 0; } -static inline int ssb_sdio_switch_core(struct ssb_bus *bus, - struct ssb_device *dev) -{ - return 0; -} static inline int ssb_sdio_scan_switch_coreidx(struct ssb_bus *bus, u8 coreidx) { return 0; -- cgit v0.10.2 From 264c7708c9ceabfd10c70f942bb2910976ca92ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Mon, 21 Sep 2015 09:47:20 +0200 Subject: ssb: drop declaration of non existing ssb_sdio_hardware_setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: Kalle Valo diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h index 1d2256e..426c805 100644 --- a/drivers/ssb/ssb_private.h +++ b/drivers/ssb/ssb_private.h @@ -133,7 +133,6 @@ extern int ssb_sdio_get_invariants(struct ssb_bus *bus, extern u32 ssb_sdio_scan_read32(struct ssb_bus *bus, u16 offset); extern int ssb_sdio_scan_switch_coreidx(struct ssb_bus *bus, u8 coreidx); -extern int ssb_sdio_hardware_setup(struct ssb_bus *bus); extern void ssb_sdio_exit(struct ssb_bus *bus); extern int ssb_sdio_init(struct ssb_bus *bus); @@ -147,10 +146,6 @@ static inline int ssb_sdio_scan_switch_coreidx(struct ssb_bus *bus, u8 coreidx) { return 0; } -static inline int ssb_sdio_hardware_setup(struct ssb_bus *bus) -{ - return 0; -} static inline void ssb_sdio_exit(struct ssb_bus *bus) { } -- cgit v0.10.2 From cf75496b6fc2fd8dd74633b0f49f4fa3f58f36e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Mon, 21 Sep 2015 09:55:26 +0200 Subject: ssb: make ssb_pcmcia_switch_core static MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: Kalle Valo diff --git a/drivers/ssb/pcmcia.c b/drivers/ssb/pcmcia.c index b413e01..f03422b 100644 --- a/drivers/ssb/pcmcia.c +++ b/drivers/ssb/pcmcia.c @@ -147,8 +147,7 @@ error: return err; } -int ssb_pcmcia_switch_core(struct ssb_bus *bus, - struct ssb_device *dev) +static int ssb_pcmcia_switch_core(struct ssb_bus *bus, struct ssb_device *dev) { int err; diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h index 426c805..8a2ebc8 100644 --- a/drivers/ssb/ssb_private.h +++ b/drivers/ssb/ssb_private.h @@ -85,8 +85,6 @@ static inline int ssb_pci_init(struct ssb_bus *bus) /* pcmcia.c */ #ifdef CONFIG_SSB_PCMCIAHOST -extern int ssb_pcmcia_switch_core(struct ssb_bus *bus, - struct ssb_device *dev); extern int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus, u8 coreidx); extern int ssb_pcmcia_switch_segment(struct ssb_bus *bus, @@ -98,11 +96,6 @@ extern void ssb_pcmcia_exit(struct ssb_bus *bus); extern int ssb_pcmcia_init(struct ssb_bus *bus); extern const struct ssb_bus_ops ssb_pcmcia_ops; #else /* CONFIG_SSB_PCMCIAHOST */ -static inline int ssb_pcmcia_switch_core(struct ssb_bus *bus, - struct ssb_device *dev) -{ - return 0; -} static inline int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus, u8 coreidx) { -- cgit v0.10.2 From 4a0f2ea79797642341020eae5918871db94fc769 Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Mon, 21 Sep 2015 15:44:24 +0200 Subject: airo: fix IW_AUTH_ALG_OPEN_SYSTEM IW_AUTH_ALG_OPEN_SYSTEM is ambiguous in set_auth for WEP as wpa_supplicant uses it for both no encryption and WEP open system. Cache the last mode set (only of these two) and use it here. This allows wpa_supplicant to work with unencrypted APs. Signed-off-by: Ondrej Zary Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index d0c97c2..67001a8 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -1237,6 +1237,7 @@ struct airo_info { int wep_capable; int max_wep_idx; + int last_auth; /* WPA-related stuff */ unsigned int bssListFirst; @@ -3786,6 +3787,16 @@ badrx: } } +static inline void set_auth_type(struct airo_info *local, int auth_type) +{ + local->config.authType = auth_type; + /* Cache the last auth type used (of AUTH_OPEN and AUTH_ENCRYPT). + * Used by airo_set_auth() + */ + if (auth_type == AUTH_OPEN || auth_type == AUTH_ENCRYPT) + local->last_auth = auth_type; +} + static u16 setup_card(struct airo_info *ai, u8 *mac, int lock) { Cmd cmd; @@ -3862,7 +3873,7 @@ static u16 setup_card(struct airo_info *ai, u8 *mac, int lock) "level scale"); } ai->config.opmode = adhoc ? MODE_STA_IBSS : MODE_STA_ESS; - ai->config.authType = AUTH_OPEN; + set_auth_type(ai, AUTH_OPEN); ai->config.modulation = MOD_CCK; if (le16_to_cpu(cap_rid.len) >= sizeof(cap_rid) && @@ -4880,13 +4891,13 @@ static void proc_config_on_close(struct inode *inode, struct file *file) line += 5; switch( line[0] ) { case 's': - ai->config.authType = AUTH_SHAREDKEY; + set_auth_type(ai, AUTH_SHAREDKEY); break; case 'e': - ai->config.authType = AUTH_ENCRYPT; + set_auth_type(ai, AUTH_ENCRYPT); break; default: - ai->config.authType = AUTH_OPEN; + set_auth_type(ai, AUTH_OPEN); break; } set_bit (FLAG_COMMIT, &ai->flags); @@ -6368,9 +6379,8 @@ static int airo_set_encode(struct net_device *dev, * should be enabled (user may turn it off later) * This is also how "iwconfig ethX key on" works */ if((index == current_index) && (key.len > 0) && - (local->config.authType == AUTH_OPEN)) { - local->config.authType = AUTH_ENCRYPT; - } + (local->config.authType == AUTH_OPEN)) + set_auth_type(local, AUTH_ENCRYPT); } else { /* Do we want to just set the transmit key index ? */ int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; @@ -6389,12 +6399,12 @@ static int airo_set_encode(struct net_device *dev, } } /* Read the flags */ - if(dwrq->flags & IW_ENCODE_DISABLED) - local->config.authType = AUTH_OPEN; // disable encryption + if (dwrq->flags & IW_ENCODE_DISABLED) + set_auth_type(local, AUTH_OPEN); /* disable encryption */ if(dwrq->flags & IW_ENCODE_RESTRICTED) - local->config.authType = AUTH_SHAREDKEY; // Only Both - if(dwrq->flags & IW_ENCODE_OPEN) - local->config.authType = AUTH_ENCRYPT; // Only Wep + set_auth_type(local, AUTH_SHAREDKEY); /* Only Both */ + if (dwrq->flags & IW_ENCODE_OPEN) + set_auth_type(local, AUTH_ENCRYPT); /* Only Wep */ /* Commit the changes to flags if needed */ if (local->config.authType != currentAuthType) set_bit (FLAG_COMMIT, &local->flags); @@ -6549,12 +6559,12 @@ static int airo_set_encodeext(struct net_device *dev, } /* Read the flags */ - if(encoding->flags & IW_ENCODE_DISABLED) - local->config.authType = AUTH_OPEN; // disable encryption + if (encoding->flags & IW_ENCODE_DISABLED) + set_auth_type(local, AUTH_OPEN); /* disable encryption */ if(encoding->flags & IW_ENCODE_RESTRICTED) - local->config.authType = AUTH_SHAREDKEY; // Only Both - if(encoding->flags & IW_ENCODE_OPEN) - local->config.authType = AUTH_ENCRYPT; // Only Wep + set_auth_type(local, AUTH_SHAREDKEY); /* Only Both */ + if (encoding->flags & IW_ENCODE_OPEN) + set_auth_type(local, AUTH_ENCRYPT); /* Commit the changes to flags if needed */ if (local->config.authType != currentAuthType) set_bit (FLAG_COMMIT, &local->flags); @@ -6659,9 +6669,9 @@ static int airo_set_auth(struct net_device *dev, if (param->value) { /* Only change auth type if unencrypted */ if (currentAuthType == AUTH_OPEN) - local->config.authType = AUTH_ENCRYPT; + set_auth_type(local, AUTH_ENCRYPT); } else { - local->config.authType = AUTH_OPEN; + set_auth_type(local, AUTH_OPEN); } /* Commit the changes to flags if needed */ @@ -6670,13 +6680,14 @@ static int airo_set_auth(struct net_device *dev, break; case IW_AUTH_80211_AUTH_ALG: { - /* FIXME: What about AUTH_OPEN? This API seems to - * disallow setting our auth to AUTH_OPEN. - */ if (param->value & IW_AUTH_ALG_SHARED_KEY) { - local->config.authType = AUTH_SHAREDKEY; + set_auth_type(local, AUTH_SHAREDKEY); } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) { - local->config.authType = AUTH_ENCRYPT; + /* We don't know here if WEP open system or + * unencrypted mode was requested - so use the + * last mode (of these two) used last time + */ + set_auth_type(local, local->last_auth); } else return -EINVAL; -- cgit v0.10.2 From 2b8fa9e870b7dd4ea30a0c72e3a134533552278f Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Mon, 21 Sep 2015 15:44:25 +0200 Subject: airo: Implement netif_carrier_on/off Add calls to netif_carrier_on and netif_carrier_off Signed-off-by: Ondrej Zary Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index 67001a8..8ae838d 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -3267,6 +3267,7 @@ static void airo_handle_link(struct airo_info *ai) wake_up_interruptible(&ai->thr_wait); } else airo_send_event(ai->dev); + netif_carrier_on(ai->dev); } else if (!scan_forceloss) { if (auto_wep && !ai->expires) { ai->expires = RUN_AT(3*HZ); @@ -3277,6 +3278,9 @@ static void airo_handle_link(struct airo_info *ai) eth_zero_addr(wrqu.ap_addr.sa_data); wrqu.ap_addr.sa_family = ARPHRD_ETHER; wireless_send_event(ai->dev, SIOCGIWAP, &wrqu, NULL); + netif_carrier_off(ai->dev); + } else { + netif_carrier_off(ai->dev); } } @@ -3613,6 +3617,7 @@ static void disable_MAC( struct airo_info *ai, int lock ) { return; if (test_bit(FLAG_ENABLED, &ai->flags)) { + netif_carrier_off(ai->dev); memset(&cmd, 0, sizeof(cmd)); cmd.cmd = MAC_DISABLE; // disable in case already enabled issuecommand(ai, &cmd, &rsp); -- cgit v0.10.2 From 1f9c6e1bc1ba5f8a10fcd6e99d170954d7c6d382 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 21 Sep 2015 19:19:53 +0300 Subject: mwifiex: fix mwifiex_rdeeprom_read() There were several bugs here. 1) The done label was in the wrong place so we didn't copy any information out when there was no command given. 2) We were using PAGE_SIZE as the size of the buffer instead of "PAGE_SIZE - pos". 3) snprintf() returns the number of characters that would have been printed if there were enough space. If there was not enough space (and we had fixed the memory corruption bug #2) then it would result in an information leak when we do simple_read_from_buffer(). I've changed it to use scnprintf() instead. I also removed the initialization at the start of the function, because I thought it made the code a little more clear. Fixes: 5e6e3a92b9a4 ('wireless: mwifiex: initial commit for Marvell mwifiex driver') Signed-off-by: Dan Carpenter Acked-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c index 5a0636d4..5583856 100644 --- a/drivers/net/wireless/mwifiex/debugfs.c +++ b/drivers/net/wireless/mwifiex/debugfs.c @@ -731,7 +731,7 @@ mwifiex_rdeeprom_read(struct file *file, char __user *ubuf, (struct mwifiex_private *) file->private_data; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *) addr; - int pos = 0, ret = 0, i; + int pos, ret, i; u8 value[MAX_EEPROM_DATA]; if (!buf) @@ -739,7 +739,7 @@ mwifiex_rdeeprom_read(struct file *file, char __user *ubuf, if (saved_offset == -1) { /* No command has been given */ - pos += snprintf(buf, PAGE_SIZE, "0"); + pos = snprintf(buf, PAGE_SIZE, "0"); goto done; } @@ -748,17 +748,17 @@ mwifiex_rdeeprom_read(struct file *file, char __user *ubuf, (u16) saved_bytes, value); if (ret) { ret = -EINVAL; - goto done; + goto out_free; } - pos += snprintf(buf, PAGE_SIZE, "%d %d ", saved_offset, saved_bytes); + pos = snprintf(buf, PAGE_SIZE, "%d %d ", saved_offset, saved_bytes); for (i = 0; i < saved_bytes; i++) - pos += snprintf(buf + strlen(buf), PAGE_SIZE, "%d ", value[i]); - - ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); + pos += scnprintf(buf + pos, PAGE_SIZE - pos, "%d ", value[i]); done: + ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); +out_free: free_page(addr); return ret; } -- cgit v0.10.2 From fbef168fec837ae26c8725737cd4b49dc8a0f917 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Tue, 29 Sep 2015 15:05:44 +0200 Subject: Bluetooth: Add hci_cmd_sync function Send a HCI command and wait for command complete event. This function serializes the requests by grabbing the req_lock. Signed-off-by: Loic Poulain Signed-off-by: Marcel Holtmann diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 256e673..61dc786 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1349,6 +1349,9 @@ void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb); void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode); +struct sk_buff *hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen, + const void *param, u32 timeout); + /* ----- HCI Sockets ----- */ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb); void hci_send_to_channel(unsigned short channel, struct sk_buff *skb, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index a7cdd99..7935646 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3580,6 +3580,25 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode) return hdev->sent_cmd->data + HCI_COMMAND_HDR_SIZE; } +/* Send HCI command and wait for command commplete event */ +struct sk_buff *hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen, + const void *param, u32 timeout) +{ + struct sk_buff *skb; + + if (!test_bit(HCI_UP, &hdev->flags)) + return ERR_PTR(-ENETDOWN); + + bt_dev_dbg(hdev, "opcode 0x%4.4x plen %d", opcode, plen); + + hci_req_lock(hdev); + skb = __hci_cmd_sync(hdev, opcode, plen, param, timeout); + hci_req_unlock(hdev); + + return skb; +} +EXPORT_SYMBOL(hci_cmd_sync); + /* Send ACL data */ static void hci_add_acl_hdr(struct sk_buff *skb, __u16 handle, __u16 flags) { -- cgit v0.10.2 From 35afa588624c4f9e19a0edfbb51769b59c90bb0d Mon Sep 17 00:00:00 2001 From: Helmut Schaa Date: Wed, 9 Sep 2015 09:46:32 +0200 Subject: mac80211: Copy tx'ed beacons to monitor mode When debugging wireless powersave issues on the AP side it's quite helpful to see our own beacons that are transmitted by the hardware/driver. However, this is not that easy since beacons don't pass through the regular TX queues. Preferably drivers would call ieee80211_tx_status also for tx'ed beacons but that's not always possible. Hence, just send a copy of each beacon generated by ieee80211_beacon_get_tim to monitor devices when they are getting fetched by the driver. Also add a HW flag IEEE80211_HW_BEACON_TX_STATUS that can be used by drivers to indicate that they report TX status for beacons. Signed-off-by: Helmut Schaa (with a fix from Christian Lamparted rolled in) Signed-off-by: Johannes Berg diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 3424ac6..301fceb 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1898,6 +1898,9 @@ struct ieee80211_txq { * @IEEE80211_HW_SUPPORTS_AMSDU_IN_AMPDU: The driver supports receiving A-MSDUs * within A-MPDU. * + * @IEEE80211_HW_BEACON_TX_STATUS: The device/driver provides TX status + * for sent beacons. + * * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays */ enum ieee80211_hw_flags { @@ -1932,6 +1935,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS, IEEE80211_HW_TDLS_WIDER_BW, IEEE80211_HW_SUPPORTS_AMSDU_IN_AMPDU, + IEEE80211_HW_BEACON_TX_STATUS, /* keep last, obviously */ NUM_IEEE80211_HW_FLAGS diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 41726fd..3636b45 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -124,6 +124,7 @@ static const char *hw_flag_names[NUM_IEEE80211_HW_FLAGS + 1] = { FLAG(SINGLE_SCAN_ON_ALL_BANDS), FLAG(TDLS_WIDER_BW), FLAG(SUPPORTS_AMSDU_IN_AMPDU), + FLAG(BEACON_TX_STATUS), /* keep last for the build bug below */ (void *)0x1 diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 84e0e8c..7354072 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -3512,6 +3512,12 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, { struct ieee80211_mutable_offsets offs = {}; struct sk_buff *bcn = __ieee80211_beacon_get(hw, vif, &offs, false); + struct sk_buff *copy; + struct ieee80211_supported_band *sband; + int shift; + + if (!bcn) + return bcn; if (tim_offset) *tim_offset = offs.tim_offset; @@ -3519,6 +3525,19 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, if (tim_length) *tim_length = offs.tim_length; + if (ieee80211_hw_check(hw, BEACON_TX_STATUS) || + !hw_to_local(hw)->monitors) + return bcn; + + /* send a copy to monitor interfaces */ + copy = skb_copy(bcn, GFP_ATOMIC); + if (!copy) + return bcn; + + shift = ieee80211_vif_get_shift(vif); + sband = hw->wiphy->bands[ieee80211_get_sdata_band(vif_to_sdata(vif))]; + ieee80211_tx_monitor(hw_to_local(hw), copy, sband, 1, shift, false); + return bcn; } EXPORT_SYMBOL(ieee80211_beacon_get_tim); -- cgit v0.10.2 From b23dcd4aca1854cda520def01731ad035cae94d8 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 18 Sep 2015 15:19:34 +0200 Subject: mac80211: Deinline drv_conf_tx() With this .config: http://busybox.net/~vda/kernel_config_ALLYES_Os, after deinlining the function size is 785 bytes and there are 7 callsites. Total size reduction is about 3.5 kbytes. Signed-off-by: Denys Vlasenko CC: John Linville CC: Michal Kazior CC: Johannes Berg CC: linux-wireless@vger.kernel.org CC: linux-kernel@vger.kernel.org Signed-off-by: Johannes Berg diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c index 267c3b1..b28e66ca 100644 --- a/net/mac80211/driver-ops.c +++ b/net/mac80211/driver-ops.c @@ -39,3 +39,28 @@ int drv_sta_state(struct ieee80211_local *local, trace_drv_return_int(local, ret); return ret; } + +int drv_conf_tx(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, u16 ac, + const struct ieee80211_tx_queue_params *params) +{ + int ret = -EOPNOTSUPP; + + might_sleep(); + + if (!check_sdata_in_driver(sdata)) + return -EIO; + + if (WARN_ONCE(params->cw_min == 0 || + params->cw_min > params->cw_max, + "%s: invalid CW_min/CW_max: %d/%d\n", + sdata->name, params->cw_min, params->cw_max)) + return -EINVAL; + + trace_drv_conf_tx(local, sdata, ac, params); + if (local->ops->conf_tx) + ret = local->ops->conf_tx(&local->hw, &sdata->vif, + ac, params); + trace_drv_return_int(local, ret); + return ret; +} diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 31482e2..6cc6bd4 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -646,30 +646,9 @@ static inline void drv_sta_statistics(struct ieee80211_local *local, trace_drv_return_void(local); } -static inline int drv_conf_tx(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata, u16 ac, - const struct ieee80211_tx_queue_params *params) -{ - int ret = -EOPNOTSUPP; - - might_sleep(); - - if (!check_sdata_in_driver(sdata)) - return -EIO; - - if (WARN_ONCE(params->cw_min == 0 || - params->cw_min > params->cw_max, - "%s: invalid CW_min/CW_max: %d/%d\n", - sdata->name, params->cw_min, params->cw_max)) - return -EINVAL; - - trace_drv_conf_tx(local, sdata, ac, params); - if (local->ops->conf_tx) - ret = local->ops->conf_tx(&local->hw, &sdata->vif, - ac, params); - trace_drv_return_int(local, ret); - return ret; -} +int drv_conf_tx(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, u16 ac, + const struct ieee80211_tx_queue_params *params); static inline u64 drv_get_tsf(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) -- cgit v0.10.2 From 4fbd572c29bd184146e8adf52631db193c4e34b9 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 18 Sep 2015 15:19:35 +0200 Subject: mac80211: Deinline drv_sta_rc_update() With this .config: http://busybox.net/~vda/kernel_config_ALLYES_Os, after deinlining the function size is 706 bytes and there are 2 callsites, reducing code size by about 700 bytes. Signed-off-by: Denys Vlasenko CC: John Linville CC: Michal Kazior CC: Johannes Berg CC: linux-wireless@vger.kernel.org CC: linux-kernel@vger.kernel.org Signed-off-by: Johannes Berg diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c index b28e66ca..b85f6ff 100644 --- a/net/mac80211/driver-ops.c +++ b/net/mac80211/driver-ops.c @@ -40,6 +40,26 @@ int drv_sta_state(struct ieee80211_local *local, return ret; } +void drv_sta_rc_update(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta *sta, u32 changed) +{ + sdata = get_bss_sdata(sdata); + if (!check_sdata_in_driver(sdata)) + return; + + WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED && + (sdata->vif.type != NL80211_IFTYPE_ADHOC && + sdata->vif.type != NL80211_IFTYPE_MESH_POINT)); + + trace_drv_sta_rc_update(local, sdata, sta, changed); + if (local->ops->sta_rc_update) + local->ops->sta_rc_update(&local->hw, &sdata->vif, + sta, changed); + + trace_drv_return_void(local); +} + int drv_conf_tx(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, u16 ac, const struct ieee80211_tx_queue_params *params) diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 6cc6bd4..2937bcb 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -596,25 +596,9 @@ int drv_sta_state(struct ieee80211_local *local, enum ieee80211_sta_state old_state, enum ieee80211_sta_state new_state); -static inline void drv_sta_rc_update(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata, - struct ieee80211_sta *sta, u32 changed) -{ - sdata = get_bss_sdata(sdata); - if (!check_sdata_in_driver(sdata)) - return; - - WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED && - (sdata->vif.type != NL80211_IFTYPE_ADHOC && - sdata->vif.type != NL80211_IFTYPE_MESH_POINT)); - - trace_drv_sta_rc_update(local, sdata, sta, changed); - if (local->ops->sta_rc_update) - local->ops->sta_rc_update(&local->hw, &sdata->vif, - sta, changed); - - trace_drv_return_void(local); -} +void drv_sta_rc_update(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta *sta, u32 changed); static inline void drv_sta_rate_tbl_update(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, -- cgit v0.10.2 From 9aae296a6208188fb40da987efb6bcd92f4fb169 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 18 Sep 2015 15:19:38 +0200 Subject: mac80211: Deinline drv_add/remove/change_interface() With this .config: http://busybox.net/~vda/kernel_config_ALLYES_Os, after deinlining these functions have sizes and callsite counts as follows: drv_add_interface: 638 bytes, 5 calls drv_remove_interface: 611 bytes, 6 calls drv_change_interface: 658 bytes, 1 call Total size reduction is about 9 kbytes. Signed-off-by: Denys Vlasenko CC: John Linville CC: Michal Kazior CC: Johannes Berg CC: linux-wireless@vger.kernel.org CC: linux-kernel@vger.kernel.org Signed-off-by: Johannes Berg diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c index b85f6ff..b284e6e 100644 --- a/net/mac80211/driver-ops.c +++ b/net/mac80211/driver-ops.c @@ -8,6 +8,60 @@ #include "trace.h" #include "driver-ops.h" +int drv_add_interface(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + int ret; + + might_sleep(); + + if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN || + (sdata->vif.type == NL80211_IFTYPE_MONITOR && + !ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) && + !(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE)))) + return -EINVAL; + + trace_drv_add_interface(local, sdata); + ret = local->ops->add_interface(&local->hw, &sdata->vif); + trace_drv_return_int(local, ret); + + if (ret == 0) + sdata->flags |= IEEE80211_SDATA_IN_DRIVER; + + return ret; +} + +int drv_change_interface(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + enum nl80211_iftype type, bool p2p) +{ + int ret; + + might_sleep(); + + if (!check_sdata_in_driver(sdata)) + return -EIO; + + trace_drv_change_interface(local, sdata, type, p2p); + ret = local->ops->change_interface(&local->hw, &sdata->vif, type, p2p); + trace_drv_return_int(local, ret); + return ret; +} + +void drv_remove_interface(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + might_sleep(); + + if (!check_sdata_in_driver(sdata)) + return; + + trace_drv_remove_interface(local, sdata); + local->ops->remove_interface(&local->hw, &sdata->vif); + sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER; + trace_drv_return_void(local); +} + __must_check int drv_sta_state(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 2937bcb..0baeefd 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -137,59 +137,15 @@ static inline void drv_set_wakeup(struct ieee80211_local *local, } #endif -static inline int drv_add_interface(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata) -{ - int ret; - - might_sleep(); - - if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN || - (sdata->vif.type == NL80211_IFTYPE_MONITOR && - !ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) && - !(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE)))) - return -EINVAL; - - trace_drv_add_interface(local, sdata); - ret = local->ops->add_interface(&local->hw, &sdata->vif); - trace_drv_return_int(local, ret); - - if (ret == 0) - sdata->flags |= IEEE80211_SDATA_IN_DRIVER; - - return ret; -} - -static inline int drv_change_interface(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata, - enum nl80211_iftype type, bool p2p) -{ - int ret; - - might_sleep(); - - if (!check_sdata_in_driver(sdata)) - return -EIO; - - trace_drv_change_interface(local, sdata, type, p2p); - ret = local->ops->change_interface(&local->hw, &sdata->vif, type, p2p); - trace_drv_return_int(local, ret); - return ret; -} +int drv_add_interface(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata); -static inline void drv_remove_interface(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata) -{ - might_sleep(); - - if (!check_sdata_in_driver(sdata)) - return; +int drv_change_interface(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + enum nl80211_iftype type, bool p2p); - trace_drv_remove_interface(local, sdata); - local->ops->remove_interface(&local->hw, &sdata->vif); - sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER; - trace_drv_return_void(local); -} +void drv_remove_interface(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata); static inline int drv_config(struct ieee80211_local *local, u32 changed) { -- cgit v0.10.2 From 9f0e13546ef5773b7059b531a667ec47a5f897ee Mon Sep 17 00:00:00 2001 From: "Fu, Zhonghui" Date: Sat, 19 Sep 2015 10:40:14 +0800 Subject: net/wireless: enable wiphy device to suspend/resume asynchronously Now, PM core supports asynchronous suspend/resume mode for devices during system suspend/resume, and the power state transition of one device may be completed in separate kernel thread. PM core ensures all power state transition timing dependency between devices. This patch enables wiphy device to suspend/resume asynchronously. This can take advantage of multicore and improve system suspend/resume speed. Signed-off-by: Zhonghui Fu Signed-off-by: Johannes Berg diff --git a/net/wireless/core.c b/net/wireless/core.c index 3893409..f223026 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -419,6 +419,7 @@ use_default_name: device_initialize(&rdev->wiphy.dev); rdev->wiphy.dev.class = &ieee80211_class; rdev->wiphy.dev.platform_data = rdev; + device_enable_async_suspend(&rdev->wiphy.dev); INIT_LIST_HEAD(&rdev->destroy_list); spin_lock_init(&rdev->destroy_list_lock); -- cgit v0.10.2 From 27392719541c89595a5c03d49b599ddfe009e6b8 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 21 Sep 2015 15:50:26 +0300 Subject: mac80211: don't tear down aggregation on suspend in case of wowlan->any In case of "any" wowlan trigger, there is no reason to tear down aggregations, as we want the device to continue working normally. Similarly, there's no reason to tear down aggregations on resume, as they should have been torn down on suspend if needed. However, since the reconfiguration flow is shared with HW restart, tear down aggregations on reconfiguration when we are not resuming. To keep things working after non-wowlan suspend, keep clearing the WLAN_STA_BLOCK_BA flag. Signed-off-by: Eliad Peller Signed-off-by: Luca Coelho Signed-off-by: Johannes Berg diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index b676b9f..ad88ad4 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -23,7 +23,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) ieee80211_del_virtual_monitor(local); - if (ieee80211_hw_check(hw, AMPDU_AGGREGATION)) { + if (ieee80211_hw_check(hw, AMPDU_AGGREGATION) && + !(wowlan && wowlan->any)) { mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { set_sta_flag(sta, WLAN_STA_BLOCK_BA); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 9cabf07..62948bb 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2017,8 +2017,9 @@ int ieee80211_reconfig(struct ieee80211_local *local) mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { - ieee80211_sta_tear_down_BA_sessions( - sta, AGG_STOP_LOCAL_REQUEST); + if (!local->resuming) + ieee80211_sta_tear_down_BA_sessions( + sta, AGG_STOP_LOCAL_REQUEST); clear_sta_flag(sta, WLAN_STA_BLOCK_BA); } -- cgit v0.10.2 From a0c391b1345cfaecfb24c3c07378d80f6168fb61 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 23 Sep 2015 10:29:20 +0200 Subject: mac80211: minstrel[_ht]: remove non-ascii debugfs characters Replace the average symbol by "avg" to avoid being warned about the non-ASCII symbol all the time, line up the columns properly. (I changed my mind - the warnings are getting annoying) Signed-off-by: Johannes Berg diff --git a/net/mac80211/rc80211_minstrel_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c index 1db5f7c..820b0ab 100644 --- a/net/mac80211/rc80211_minstrel_debugfs.c +++ b/net/mac80211/rc80211_minstrel_debugfs.c @@ -85,12 +85,10 @@ minstrel_stats_open(struct inode *inode, struct file *file) file->private_data = ms; p = ms->buf; p += sprintf(p, "\n"); - p += sprintf(p, "best __________rate_________ ______" - "statistics______ ________last_______ " - "______sum-of________\n"); - p += sprintf(p, "rate [name idx airtime max_tp] [ ø(tp) ø(prob) " - "sd(prob)] [prob.|retry|suc|att] " - "[#success | #attempts]\n"); + p += sprintf(p, + "best __________rate_________ ________statistics________ ________last_______ ______sum-of________\n"); + p += sprintf(p, + "rate [name idx airtime max_tp] [avg(tp) avg(prob) sd(prob)] [prob.|retry|suc|att] [#success | #attempts]\n"); for (i = 0; i < mi->n_rates; i++) { struct minstrel_rate *mr = &mi->r[i]; @@ -112,7 +110,7 @@ minstrel_stats_open(struct inode *inode, struct file *file) prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); - p += sprintf(p, "%4u.%1u %4u.%1u %3u.%1u %3u.%1u" + p += sprintf(p, "%4u.%1u %4u.%1u %3u.%1u %3u.%1u" " %3u.%1u %3u %3u %-3u " "%9llu %-9llu\n", tp_max / 10, tp_max % 10, diff --git a/net/mac80211/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c index 6822ce0..5320e35 100644 --- a/net/mac80211/rc80211_minstrel_ht_debugfs.c +++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c @@ -86,7 +86,7 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); - p += sprintf(p, "%4u.%1u %4u.%1u %3u.%1u %3u.%1u" + p += sprintf(p, "%4u.%1u %4u.%1u %3u.%1u %3u.%1u" " %3u.%1u %3u %3u %-3u " "%9llu %-9llu\n", tp_max / 10, tp_max % 10, @@ -129,12 +129,10 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file) p = ms->buf; p += sprintf(p, "\n"); - p += sprintf(p, " best ____________rate__________ " - "______statistics______ ________last_______ " - "______sum-of________\n"); - p += sprintf(p, "mode guard # rate [name idx airtime max_tp] " - "[ ø(tp) ø(prob) sd(prob)] [prob.|retry|suc|att] [#success | " - "#attempts]\n"); + p += sprintf(p, + " best ____________rate__________ ________statistics________ ________last_______ ______sum-of________\n"); + p += sprintf(p, + "mode guard # rate [name idx airtime max_tp] [avg(tp) avg(prob) sd(prob)] [prob.|retry|suc|att] [#success | #attempts]\n"); p = minstrel_ht_stats_dump(mi, MINSTREL_CCK_GROUP, p); for (i = 0; i < MINSTREL_CCK_GROUP; i++) -- cgit v0.10.2 From 0e5c371aa05522ac14e91ddee0522ad855e12d02 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 23 Sep 2015 14:02:47 +0200 Subject: mac80211: improve __rate_control_send_low warning If there are no supported rates in the rate mask with the required flags, we warn, but it's not clear which part causes the warning. Add the relevant data to the warning to understand why it happens. Signed-off-by: Johannes Berg diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 9857693..48d053b 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -305,7 +305,10 @@ static void __rate_control_send_low(struct ieee80211_hw *hw, info->control.rates[0].idx = i; break; } - WARN_ON_ONCE(i == sband->n_bitrates); + WARN_ONCE(i == sband->n_bitrates, + "no supported rates (0x%x) in rate_mask 0x%x with flags 0x%x\n", + sta ? sta->supp_rates[sband->band] : 0, + rate_mask, rate_flags); info->control.rates[0].count = (info->flags & IEEE80211_TX_CTL_NO_ACK) ? -- cgit v0.10.2 From 42677ed33a8b6995e6af2ec15643840afcf1c48b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 23 Sep 2015 14:18:34 +0200 Subject: mac80211: Deinline drv_switch_vif_chanctx() With this .config: http://busybox.net/~vda/kernel_config_ALLYES_Os, after deinlining the function size is 821 bytes and there are 2 callsites, reducing code size by about 800 bytes. Signed-off-by: Denys Vlasenko CC: Johannes Berg CC: John Linville CC: Michal Kazior CC: linux-wireless@vger.kernel.org CC: linux-kernel@vger.kernel.org [adjust code-style a bit] Signed-off-by: Johannes Berg diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c index b284e6e..1a720e8 100644 --- a/net/mac80211/driver-ops.c +++ b/net/mac80211/driver-ops.c @@ -138,3 +138,54 @@ int drv_conf_tx(struct ieee80211_local *local, trace_drv_return_int(local, ret); return ret; } + +int drv_switch_vif_chanctx(struct ieee80211_local *local, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs, enum ieee80211_chanctx_switch_mode mode) +{ + int ret = 0; + int i; + + if (!local->ops->switch_vif_chanctx) + return -EOPNOTSUPP; + + for (i = 0; i < n_vifs; i++) { + struct ieee80211_chanctx *new_ctx = + container_of(vifs[i].new_ctx, + struct ieee80211_chanctx, + conf); + struct ieee80211_chanctx *old_ctx = + container_of(vifs[i].old_ctx, + struct ieee80211_chanctx, + conf); + + WARN_ON_ONCE(!old_ctx->driver_present); + WARN_ON_ONCE((mode == CHANCTX_SWMODE_SWAP_CONTEXTS && + new_ctx->driver_present) || + (mode == CHANCTX_SWMODE_REASSIGN_VIF && + !new_ctx->driver_present)); + } + + trace_drv_switch_vif_chanctx(local, vifs, n_vifs, mode); + ret = local->ops->switch_vif_chanctx(&local->hw, + vifs, n_vifs, mode); + trace_drv_return_int(local, ret); + + if (!ret && mode == CHANCTX_SWMODE_SWAP_CONTEXTS) { + for (i = 0; i < n_vifs; i++) { + struct ieee80211_chanctx *new_ctx = + container_of(vifs[i].new_ctx, + struct ieee80211_chanctx, + conf); + struct ieee80211_chanctx *old_ctx = + container_of(vifs[i].old_ctx, + struct ieee80211_chanctx, + conf); + + new_ctx->driver_present = true; + old_ctx->driver_present = false; + } + } + + return ret; +} diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 0baeefd..275146e 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -1002,58 +1002,9 @@ static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local, trace_drv_return_void(local); } -static inline int -drv_switch_vif_chanctx(struct ieee80211_local *local, - struct ieee80211_vif_chanctx_switch *vifs, - int n_vifs, - enum ieee80211_chanctx_switch_mode mode) -{ - int ret = 0; - int i; - - if (!local->ops->switch_vif_chanctx) - return -EOPNOTSUPP; - - for (i = 0; i < n_vifs; i++) { - struct ieee80211_chanctx *new_ctx = - container_of(vifs[i].new_ctx, - struct ieee80211_chanctx, - conf); - struct ieee80211_chanctx *old_ctx = - container_of(vifs[i].old_ctx, - struct ieee80211_chanctx, - conf); - - WARN_ON_ONCE(!old_ctx->driver_present); - WARN_ON_ONCE((mode == CHANCTX_SWMODE_SWAP_CONTEXTS && - new_ctx->driver_present) || - (mode == CHANCTX_SWMODE_REASSIGN_VIF && - !new_ctx->driver_present)); - } - - trace_drv_switch_vif_chanctx(local, vifs, n_vifs, mode); - ret = local->ops->switch_vif_chanctx(&local->hw, - vifs, n_vifs, mode); - trace_drv_return_int(local, ret); - - if (!ret && mode == CHANCTX_SWMODE_SWAP_CONTEXTS) { - for (i = 0; i < n_vifs; i++) { - struct ieee80211_chanctx *new_ctx = - container_of(vifs[i].new_ctx, - struct ieee80211_chanctx, - conf); - struct ieee80211_chanctx *old_ctx = - container_of(vifs[i].old_ctx, - struct ieee80211_chanctx, - conf); - - new_ctx->driver_present = true; - old_ctx->driver_present = false; - } - } - - return ret; -} +int drv_switch_vif_chanctx(struct ieee80211_local *local, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs, enum ieee80211_chanctx_switch_mode mode); static inline int drv_start_ap(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) -- cgit v0.10.2 From 6db96838971eb4c8ae6285795188f391e97d47c3 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 23 Sep 2015 14:18:35 +0200 Subject: mac80211: Deinline drv_ampdu_action() With this .config: http://busybox.net/~vda/kernel_config_ALLYES_Os, after deinlining the function size is 755 bytes and there are 6 callsites. Total size reduction is about 3.3 kbytes. Signed-off-by: Denys Vlasenko CC: Johannes Berg CC: John Linville CC: Michal Kazior CC: linux-wireless@vger.kernel.org CC: linux-kernel@vger.kernel.org Signed-off-by: Johannes Berg diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c index 1a720e8..e1bb9e04 100644 --- a/net/mac80211/driver-ops.c +++ b/net/mac80211/driver-ops.c @@ -189,3 +189,29 @@ int drv_switch_vif_chanctx(struct ieee80211_local *local, return ret; } + +int drv_ampdu_action(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, + u16 *ssn, u8 buf_size, bool amsdu) +{ + int ret = -EOPNOTSUPP; + + might_sleep(); + + sdata = get_bss_sdata(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; + + trace_drv_ampdu_action(local, sdata, action, sta, tid, + ssn, buf_size, amsdu); + + if (local->ops->ampdu_action) + ret = local->ops->ampdu_action(&local->hw, &sdata->vif, action, + sta, tid, ssn, buf_size, amsdu); + + trace_drv_return_int(local, ret); + + return ret; +} diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 275146e..6411c3b9 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -649,31 +649,11 @@ static inline int drv_tx_last_beacon(struct ieee80211_local *local) return ret; } -static inline int drv_ampdu_action(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, - u16 *ssn, u8 buf_size, bool amsdu) -{ - int ret = -EOPNOTSUPP; - - might_sleep(); - - sdata = get_bss_sdata(sdata); - if (!check_sdata_in_driver(sdata)) - return -EIO; - - trace_drv_ampdu_action(local, sdata, action, sta, tid, - ssn, buf_size, amsdu); - - if (local->ops->ampdu_action) - ret = local->ops->ampdu_action(&local->hw, &sdata->vif, action, - sta, tid, ssn, buf_size, amsdu); - - trace_drv_return_int(local, ret); - - return ret; -} +int drv_ampdu_action(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, + u16 *ssn, u8 buf_size, bool amsdu); static inline int drv_get_survey(struct ieee80211_local *local, int idx, struct survey_info *survey) -- cgit v0.10.2 From 416eb9fc29469f036c85b412edf89774d6b34b0f Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 23 Sep 2015 14:18:36 +0200 Subject: mac80211: Deinline drv_get/set/reset_tsf() With this .config: http://busybox.net/~vda/kernel_config_ALLYES_Os, after deinlining these functions have sizes and callsite counts as follows: drv_get_tsf: 634 bytes, 6 calls drv_set_tsf: 626 bytes, 2 calls drv_reset_tsf: 617 bytes, 2 calls Total size reduction is about 4.2 kbytes. Signed-off-by: Denys Vlasenko CC: Johannes Berg CC: John Linville CC: Michal Kazior CC: linux-wireless@vger.kernel.org CC: linux-kernel@vger.kernel.org Signed-off-by: Johannes Berg diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c index e1bb9e04..a1d5431 100644 --- a/net/mac80211/driver-ops.c +++ b/net/mac80211/driver-ops.c @@ -139,6 +139,52 @@ int drv_conf_tx(struct ieee80211_local *local, return ret; } +u64 drv_get_tsf(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + u64 ret = -1ULL; + + might_sleep(); + + if (!check_sdata_in_driver(sdata)) + return ret; + + trace_drv_get_tsf(local, sdata); + if (local->ops->get_tsf) + ret = local->ops->get_tsf(&local->hw, &sdata->vif); + trace_drv_return_u64(local, ret); + return ret; +} + +void drv_set_tsf(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + u64 tsf) +{ + might_sleep(); + + if (!check_sdata_in_driver(sdata)) + return; + + trace_drv_set_tsf(local, sdata, tsf); + if (local->ops->set_tsf) + local->ops->set_tsf(&local->hw, &sdata->vif, tsf); + trace_drv_return_void(local); +} + +void drv_reset_tsf(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + might_sleep(); + + if (!check_sdata_in_driver(sdata)) + return; + + trace_drv_reset_tsf(local, sdata); + if (local->ops->reset_tsf) + local->ops->reset_tsf(&local->hw, &sdata->vif); + trace_drv_return_void(local); +} + int drv_switch_vif_chanctx(struct ieee80211_local *local, struct ieee80211_vif_chanctx_switch *vifs, int n_vifs, enum ieee80211_chanctx_switch_mode mode) diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 6411c3b9..3098709 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -590,51 +590,13 @@ int drv_conf_tx(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, u16 ac, const struct ieee80211_tx_queue_params *params); -static inline u64 drv_get_tsf(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata) -{ - u64 ret = -1ULL; - - might_sleep(); - - if (!check_sdata_in_driver(sdata)) - return ret; - - trace_drv_get_tsf(local, sdata); - if (local->ops->get_tsf) - ret = local->ops->get_tsf(&local->hw, &sdata->vif); - trace_drv_return_u64(local, ret); - return ret; -} - -static inline void drv_set_tsf(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata, - u64 tsf) -{ - might_sleep(); - - if (!check_sdata_in_driver(sdata)) - return; - - trace_drv_set_tsf(local, sdata, tsf); - if (local->ops->set_tsf) - local->ops->set_tsf(&local->hw, &sdata->vif, tsf); - trace_drv_return_void(local); -} - -static inline void drv_reset_tsf(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata) -{ - might_sleep(); - - if (!check_sdata_in_driver(sdata)) - return; - - trace_drv_reset_tsf(local, sdata); - if (local->ops->reset_tsf) - local->ops->reset_tsf(&local->hw, &sdata->vif); - trace_drv_return_void(local); -} +u64 drv_get_tsf(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata); +void drv_set_tsf(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + u64 tsf); +void drv_reset_tsf(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata); static inline int drv_tx_last_beacon(struct ieee80211_local *local) { -- cgit v0.10.2 From d0a77c6569abe29d921148c45f598bc796084226 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 23 Sep 2015 10:42:28 +0200 Subject: mac80211: allow writing TX PN in debugfs For certain tests, for example replay detection, it can be useful to be able to influence/set the PN used in outgoing packets. Make it possible to change the TX PN in debugfs. For now, this doesn't support TKIP since I haven't needed it, but there's no reason it couldn't be added if necessary. Note that this must be used very carefully: it could, for example, be used to make "valid replays" where the PN reuse happens on a different TID. This couldn't be done by an attacker since the TID is protected as part of the AAD. Signed-off-by: Johannes Berg diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c index 702ca12..7961e7d 100644 --- a/net/mac80211/debugfs_key.c +++ b/net/mac80211/debugfs_key.c @@ -2,6 +2,7 @@ * Copyright 2003-2005 Devicescape Software, Inc. * Copyright (c) 2006 Jiri Benc * Copyright 2007 Johannes Berg + * Copyright (C) 2015 Intel Deutschland GmbH * * 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 @@ -34,6 +35,14 @@ static const struct file_operations key_ ##name## _ops = { \ .llseek = generic_file_llseek, \ } +#define KEY_OPS_W(name) \ +static const struct file_operations key_ ##name## _ops = { \ + .read = key_##name##_read, \ + .write = key_##name##_write, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +} + #define KEY_FILE(name, format) \ KEY_READ_##format(name) \ KEY_OPS(name) @@ -74,6 +83,41 @@ static ssize_t key_algorithm_read(struct file *file, } KEY_OPS(algorithm); +static ssize_t key_tx_spec_write(struct file *file, const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct ieee80211_key *key = file->private_data; + u64 pn; + int ret; + + switch (key->conf.cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + return -EINVAL; + case WLAN_CIPHER_SUITE_TKIP: + /* not supported yet */ + return -EOPNOTSUPP; + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_CCMP_256: + case WLAN_CIPHER_SUITE_AES_CMAC: + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + ret = kstrtou64_from_user(userbuf, count, 16, &pn); + if (ret) + return ret; + /* PN is a 48-bit counter */ + if (pn >= (1ULL << 48)) + return -ERANGE; + atomic64_set(&key->conf.tx_pn, pn); + return count; + default: + return 0; + } +} + static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { @@ -110,7 +154,7 @@ static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf, } return simple_read_from_buffer(userbuf, count, ppos, buf, len); } -KEY_OPS(tx_spec); +KEY_OPS_W(tx_spec); static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) @@ -278,6 +322,9 @@ KEY_OPS(key); #define DEBUGFS_ADD(name) \ debugfs_create_file(#name, 0400, key->debugfs.dir, \ key, &key_##name##_ops); +#define DEBUGFS_ADD_W(name) \ + debugfs_create_file(#name, 0600, key->debugfs.dir, \ + key, &key_##name##_ops); void ieee80211_debugfs_key_add(struct ieee80211_key *key) { @@ -310,7 +357,7 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key) DEBUGFS_ADD(keyidx); DEBUGFS_ADD(hw_key_idx); DEBUGFS_ADD(algorithm); - DEBUGFS_ADD(tx_spec); + DEBUGFS_ADD_W(tx_spec); DEBUGFS_ADD(rx_spec); DEBUGFS_ADD(replays); DEBUGFS_ADD(icverrors); -- cgit v0.10.2 From 47edb11b522561658fe719e56aa69a3c3098a3fe Mon Sep 17 00:00:00 2001 From: Ayala Beker Date: Mon, 21 Sep 2015 15:49:53 +0300 Subject: cfg80211: allow changing station capabilities for unassociated stations Currently, cfg80211 rejects capability updates for existing entries and as a result it's impossible to update entries that were added unassociated, but that is necessary to go through the full station states from userspace, adding a station before authentication etc. Fix this by allowing updates to capabilities for stations that the driver (or mac80211) assigned unassociated state. Drivers setting the full station state support flag must use the new station type for proper operation. Signed-off-by: Ayala Beker Signed-off-by: Luca Coelho Signed-off-by: Johannes Berg diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 0f54c9e..90332a1 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -858,6 +858,8 @@ struct station_del_parameters { /** * enum cfg80211_station_type - the type of station being modified * @CFG80211_STA_AP_CLIENT: client of an AP interface + * @CFG80211_STA_AP_CLIENT_UNASSOC: client of an AP interface that is still + * unassociated (update properties for this type of client is permitted) * @CFG80211_STA_AP_MLME_CLIENT: client of an AP interface that has * the AP MLME in the device * @CFG80211_STA_AP_STA: AP station on managed interface @@ -873,6 +875,7 @@ struct station_del_parameters { */ enum cfg80211_station_type { CFG80211_STA_AP_CLIENT, + CFG80211_STA_AP_CLIENT_UNASSOC, CFG80211_STA_AP_MLME_CLIENT, CFG80211_STA_AP_STA, CFG80211_STA_IBSS, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 50cd770..f05ba8b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4009,7 +4009,8 @@ int cfg80211_check_station_change(struct wiphy *wiphy, params->sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); } - if (statype != CFG80211_STA_TDLS_PEER_SETUP) { + if (statype != CFG80211_STA_TDLS_PEER_SETUP && + statype != CFG80211_STA_AP_CLIENT_UNASSOC) { /* reject other things that can't change */ if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) return -EINVAL; @@ -4021,7 +4022,8 @@ int cfg80211_check_station_change(struct wiphy *wiphy, return -EINVAL; } - if (statype != CFG80211_STA_AP_CLIENT) { + if (statype != CFG80211_STA_AP_CLIENT && + statype != CFG80211_STA_AP_CLIENT_UNASSOC) { if (params->vlan) return -EINVAL; } @@ -4033,6 +4035,7 @@ int cfg80211_check_station_change(struct wiphy *wiphy, return -EOPNOTSUPP; break; case CFG80211_STA_AP_CLIENT: + case CFG80211_STA_AP_CLIENT_UNASSOC: /* accept only the listed bits */ if (params->sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | -- cgit v0.10.2 From 44674d9c2267f454f38df7b2395939bfa911f92e Mon Sep 17 00:00:00 2001 From: Ayala Beker Date: Wed, 23 Sep 2015 10:41:27 +0200 Subject: mac80211: advertise support for full station state in AP mode This enables adding stations in unauthenticated mode, just after receiving the first authentication frame; which in turn allows sending a negative authentication reply if the station cannot be added. In addition init rate control for unassociated station only when it becomes associated, prior to that low rates will be used. Signed-off-by: Ayala Beker Signed-off-by: Johannes Berg diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 9eab783..1b91fcd 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -981,7 +981,7 @@ static int sta_apply_auth_flags(struct ieee80211_local *local, * well. Some drivers require rate control initialized * before drv_sta_state() is called. */ - if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) + if (!test_sta_flag(sta, WLAN_STA_RATE_CONTROL)) rate_control_rate_init(sta); ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC); @@ -1120,8 +1120,11 @@ static int sta_apply_parameters(struct ieee80211_local *local, local->hw.queues >= IEEE80211_NUM_ACS) sta->sta.wme = set & BIT(NL80211_STA_FLAG_WME); - /* auth flags will be set later for TDLS stations */ - if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { + /* auth flags will be set later for TDLS, + * and for unassociated stations that move to assocaited */ + if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER) && + !((mask & BIT(NL80211_STA_FLAG_ASSOCIATED)) && + (set & BIT(NL80211_STA_FLAG_ASSOCIATED)))) { ret = sta_apply_auth_flags(local, sta, mask, set); if (ret) return ret; @@ -1213,7 +1216,8 @@ static int sta_apply_parameters(struct ieee80211_local *local, sta_apply_mesh_params(local, sta, params); /* set the STA state after all sta info from usermode has been set */ - if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { + if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) || + set & BIT(NL80211_STA_FLAG_ASSOCIATED)) { ret = sta_apply_auth_flags(local, sta, mask, set); if (ret) return ret; @@ -1255,12 +1259,14 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, * defaults -- if userspace wants something else we'll * change it accordingly in sta_apply_parameters() */ - if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) { + if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) && + !(params->sta_flags_set & (BIT(NL80211_STA_FLAG_AUTHENTICATED) | + BIT(NL80211_STA_FLAG_ASSOCIATED)))) { sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); - } else { - sta->sta.tdls = true; } + if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) + sta->sta.tdls = true; err = sta_apply_parameters(local, sta, params); if (err) { @@ -1269,10 +1275,12 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, } /* - * for TDLS, rate control should be initialized only when - * rates are known and station is marked authorized + * for TDLS and for unassociated station, rate control should be + * initialized only when rates are known and station is marked + * authorized/associated */ - if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) + if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER) && + test_sta_flag(sta, WLAN_STA_ASSOC)) rate_control_rate_init(sta); layer2_update = sdata->vif.type == NL80211_IFTYPE_AP_VLAN || @@ -1347,7 +1355,10 @@ static int ieee80211_change_station(struct wiphy *wiphy, break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: - statype = CFG80211_STA_AP_CLIENT; + if (test_sta_flag(sta, WLAN_STA_ASSOC)) + statype = CFG80211_STA_AP_CLIENT; + else + statype = CFG80211_STA_AP_CLIENT_UNASSOC; break; default: err = -EOPNOTSUPP; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index ff79a13..9b813a2 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -543,7 +543,8 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, NL80211_FEATURE_HT_IBSS | NL80211_FEATURE_VIF_TXPOWER | NL80211_FEATURE_MAC_ON_CREATE | - NL80211_FEATURE_USERSPACE_MPM; + NL80211_FEATURE_USERSPACE_MPM | + NL80211_FEATURE_FULL_AP_CLIENT_STATE; if (!ops->hw_scan) wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN | -- cgit v0.10.2 From 50f36ae61a5b65ba4612a5d2aa696c8ac5b6c988 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 24 Sep 2015 14:59:48 +0200 Subject: mac80211: fix tx sequence number assignment with software queue + fast-xmit When using software queueing, tx sequence number assignment happens at ieee80211_tx_dequeue time, so the fast-xmit codepath must not do that. Signed-off-by: Felix Fietkau Signed-off-by: Johannes Berg diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 7354072..464ba1a 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2767,7 +2767,8 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata, if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) { *ieee80211_get_qos_ctl(hdr) = tid; - hdr->seq_ctrl = ieee80211_tx_next_seq(sta, tid); + if (!sta->sta.txq[0]) + hdr->seq_ctrl = ieee80211_tx_next_seq(sta, tid); } else { info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ; hdr->seq_ctrl = cpu_to_le16(sdata->sequence_number); -- cgit v0.10.2 From 90d13e8f5b3c2445f481be4a2012a1861337f718 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 24 Sep 2015 16:13:07 +0200 Subject: mac80211: reduce indentation by inlining a check Instead of nesting two if statements, inline the second check into the first if statement and to indentation. Signed-off-by: Johannes Berg diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index c7d316b..88894d9 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3437,31 +3437,27 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, len - baselen, false, &elems, care_about_ies, ncrc); - if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK)) { - bool directed_tim = ieee80211_check_tim(elems.tim, - elems.tim_len, - ifmgd->aid); - if (directed_tim) { - if (local->hw.conf.dynamic_ps_timeout > 0) { - if (local->hw.conf.flags & IEEE80211_CONF_PS) { - local->hw.conf.flags &= ~IEEE80211_CONF_PS; - ieee80211_hw_config(local, - IEEE80211_CONF_CHANGE_PS); - } - ieee80211_send_nullfunc(local, sdata, 0); - } else if (!local->pspolling && sdata->u.mgd.powersave) { - local->pspolling = true; - - /* - * Here is assumed that the driver will be - * able to send ps-poll frame and receive a - * response even though power save mode is - * enabled, but some drivers might require - * to disable power save here. This needs - * to be investigated. - */ - ieee80211_send_pspoll(local, sdata); + if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) && + ieee80211_check_tim(elems.tim, elems.tim_len, ifmgd->aid)) { + if (local->hw.conf.dynamic_ps_timeout > 0) { + if (local->hw.conf.flags & IEEE80211_CONF_PS) { + local->hw.conf.flags &= ~IEEE80211_CONF_PS; + ieee80211_hw_config(local, + IEEE80211_CONF_CHANGE_PS); } + ieee80211_send_nullfunc(local, sdata, 0); + } else if (!local->pspolling && sdata->u.mgd.powersave) { + local->pspolling = true; + + /* + * Here is assumed that the driver will be + * able to send ps-poll frame and receive a + * response even though power save mode is + * enabled, but some drivers might require + * to disable power save here. This needs + * to be investigated. + */ + ieee80211_send_pspoll(local, sdata); } } -- cgit v0.10.2 From 076cdcb12f784b2057f172b5caca641fafa67cdf Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 24 Sep 2015 16:14:55 +0200 Subject: mac80211: use bool argument to ieee80211_send_nullfunc Instead of int with 0/1, use bool with false/true for the powersave argument to ieee80211_send_nullfunc(). Signed-off-by: Johannes Berg diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 1af655a..f9605f1 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1850,7 +1850,7 @@ void ieee80211_dynamic_ps_disable_work(struct work_struct *work); void ieee80211_dynamic_ps_timer(unsigned long data); void ieee80211_send_nullfunc(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, - int powersave); + bool powersave); void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, struct ieee80211_hdr *hdr); void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 88894d9..88047bf 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -936,7 +936,7 @@ void ieee80211_send_pspoll(struct ieee80211_local *local, void ieee80211_send_nullfunc(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, - int powersave) + bool powersave) { struct sk_buff *skb; struct ieee80211_hdr_3addr *nullfunc; @@ -1420,7 +1420,7 @@ static void ieee80211_enable_ps(struct ieee80211_local *local, msecs_to_jiffies(conf->dynamic_ps_timeout)); } else { if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK)) - ieee80211_send_nullfunc(local, sdata, 1); + ieee80211_send_nullfunc(local, sdata, true); if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) && ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) @@ -1635,7 +1635,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work) msecs_to_jiffies( local->hw.conf.dynamic_ps_timeout)); } else { - ieee80211_send_nullfunc(local, sdata, 1); + ieee80211_send_nullfunc(local, sdata, true); /* Flush to get the tx status of nullfunc frame */ ieee80211_flush_queues(local, sdata, false); } @@ -2268,7 +2268,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) if (ieee80211_hw_check(&sdata->local->hw, REPORTS_TX_ACK_STATUS)) { ifmgd->nullfunc_failed = false; - ieee80211_send_nullfunc(sdata->local, sdata, 0); + ieee80211_send_nullfunc(sdata->local, sdata, false); } else { int ssid_len; @@ -3445,7 +3445,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); } - ieee80211_send_nullfunc(local, sdata, 0); + ieee80211_send_nullfunc(local, sdata, false); } else if (!local->pspolling && sdata->u.mgd.powersave) { local->pspolling = true; diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index f2c75cf..0440103 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -57,7 +57,7 @@ static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata) * to send a new nullfunc frame to inform the AP that we * are again sleeping. */ - ieee80211_send_nullfunc(local, sdata, 1); + ieee80211_send_nullfunc(local, sdata, true); } /* inform AP that we are awake again, unless power save is enabled */ @@ -66,7 +66,7 @@ static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata) struct ieee80211_local *local = sdata->local; if (!local->ps_sdata) - ieee80211_send_nullfunc(local, sdata, 0); + ieee80211_send_nullfunc(local, sdata, false); else if (local->offchannel_ps_enabled) { /* * In !IEEE80211_HW_PS_NULLFUNC_STACK case the hardware @@ -93,7 +93,7 @@ static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata) * restart the timer now and send a nullfunc frame to inform * the AP that we are awake. */ - ieee80211_send_nullfunc(local, sdata, 0); + ieee80211_send_nullfunc(local, sdata, false); mod_timer(&local->dynamic_ps_timer, jiffies + msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout)); } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 62948bb..60c4dbf 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1966,7 +1966,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) if (!sdata->u.mgd.associated) continue; - ieee80211_send_nullfunc(local, sdata, 0); + ieee80211_send_nullfunc(local, sdata, false); } } -- cgit v0.10.2 From 188515fbc6b18e6bc6f2fa4629f1a77308197371 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 14 Sep 2015 20:08:51 -0500 Subject: openvswitch: Pass net into ovs_vport_output When struct net starts being passed through the ipv4 and ipv6 fragment routines ovs_vport_output will need to take a net parameter. Prepare ovs_vport_output before that is needed and introduce ovs_vport_output_skk for the call sites that still need the old calling conventions. Signed-off-by: "Eric W. Biederman" diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 315f533..f00c641 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -620,7 +620,7 @@ static int set_sctp(struct sk_buff *skb, struct sw_flow_key *flow_key, return 0; } -static int ovs_vport_output(struct sock *sock, struct sk_buff *skb) +static int ovs_vport_output(struct net *net, struct sock *sk, struct sk_buff *skb) { struct ovs_frag_data *data = this_cpu_ptr(&ovs_frag_data_storage); struct vport *vport = data->vport; @@ -645,6 +645,11 @@ static int ovs_vport_output(struct sock *sock, struct sk_buff *skb) ovs_vport_send(vport, skb); return 0; } +static int ovs_vport_output_sk(struct sock *sk, struct sk_buff *skb) +{ + struct net *net = dev_net(skb_dst(skb)->dev); + return ovs_vport_output(net, sk, skb); +} static unsigned int ovs_dst_get_mtu(const struct dst_entry *dst) @@ -700,7 +705,7 @@ static void ovs_fragment(struct vport *vport, struct sk_buff *skb, u16 mru, skb_dst_set_noref(skb, &ovs_dst); IPCB(skb)->frag_max_size = mru; - ip_do_fragment(skb->sk, skb, ovs_vport_output); + ip_do_fragment(skb->sk, skb, ovs_vport_output_sk); refdst_drop(orig_dst); } else if (ethertype == htons(ETH_P_IPV6)) { const struct nf_ipv6_ops *v6ops = nf_get_ipv6_ops(); @@ -722,7 +727,7 @@ static void ovs_fragment(struct vport *vport, struct sk_buff *skb, u16 mru, skb_dst_set_noref(skb, &ovs_rt.dst); IP6CB(skb)->frag_max_size = mru; - v6ops->fragment(skb->sk, skb, ovs_vport_output); + v6ops->fragment(skb->sk, skb, ovs_vport_output_sk); refdst_drop(orig_dst); } else { WARN_ONCE(1, "Failed fragment ->%s: eth=%04x, MRU=%d, MTU=%d.", -- cgit v0.10.2 From c559cd3ad32ba729bb810283c5fc6838d2473c2e Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 14 Sep 2015 20:10:28 -0500 Subject: openvswitch: Pass net into ovs_fragment In preparation for the ipv4 and ipv6 fragmentation code taking a net parameter pass a struct net into ovs_fragment where the v4 and v6 fragmentation code is called. Signed-off-by: "Eric W. Biederman" diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index f00c641..ba38662 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -684,8 +684,8 @@ static void prepare_frag(struct vport *vport, struct sk_buff *skb) skb_pull(skb, hlen); } -static void ovs_fragment(struct vport *vport, struct sk_buff *skb, u16 mru, - __be16 ethertype) +static void ovs_fragment(struct net *net, struct vport *vport, + struct sk_buff *skb, u16 mru, __be16 ethertype) { if (skb_network_offset(skb) > MAX_L2_LEN) { OVS_NLERR(1, "L2 header too long to fragment"); @@ -748,6 +748,7 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port, if (likely(!mru || (skb->len <= mru + ETH_HLEN))) { 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)) { @@ -757,7 +758,7 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port, ethertype = vlan_get_protocol(skb); } - ovs_fragment(vport, skb, mru, ethertype); + ovs_fragment(net, vport, skb, mru, ethertype); } else { kfree_skb(skb); } -- cgit v0.10.2 From 694869b3c5440e0d821583ec8811b6cb5d03742d Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 12 Jun 2015 21:55:31 -0500 Subject: ipv4: Pass struct net through ip_fragment Signed-off-by: "Eric W. Biederman" diff --git a/include/net/ip.h b/include/net/ip.h index 91a6b2c..b783141 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -109,8 +109,8 @@ int ip_local_deliver(struct sk_buff *skb); int ip_mr_input(struct sk_buff *skb); int ip_output(struct sock *sk, struct sk_buff *skb); int ip_mc_output(struct sock *sk, struct sk_buff *skb); -int ip_do_fragment(struct sock *sk, struct sk_buff *skb, - int (*output)(struct sock *, struct sk_buff *)); +int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, + int (*output)(struct net *, struct sock *, struct sk_buff *)); void ip_send_check(struct iphdr *ip); int __ip_local_out(struct sk_buff *skb); int ip_local_out_sk(struct sock *sk, struct sk_buff *skb); diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c index 13f0367..00e356c 100644 --- a/net/bridge/br_netfilter_hooks.c +++ b/net/bridge/br_netfilter_hooks.c @@ -701,7 +701,7 @@ static int br_nf_push_frag_xmit_sk(struct sock *sk, struct sk_buff *skb) #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) static int br_nf_ip_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, - int (*output)(struct sock *, struct sk_buff *)) + int (*output)(struct net *, struct sock *, struct sk_buff *)) { unsigned int mtu = ip_skb_dst_mtu(skb); struct iphdr *iph = ip_hdr(skb); @@ -714,7 +714,7 @@ br_nf_ip_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, return -EMSGSIZE; } - return ip_do_fragment(sk, skb, output); + return ip_do_fragment(net, sk, skb, output); } #endif @@ -763,7 +763,7 @@ static int br_nf_dev_queue_xmit(struct net *net, struct sock *sk, struct sk_buff skb_copy_from_linear_data_offset(skb, -data->size, data->mac, data->size); - return br_nf_ip_fragment(net, sk, skb, br_nf_push_frag_xmit_sk); + return br_nf_ip_fragment(net, sk, skb, br_nf_push_frag_xmit); } #endif #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index aff6766..911ea73 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -83,9 +83,10 @@ int sysctl_ip_default_ttl __read_mostly = IPDEFTTL; EXPORT_SYMBOL(sysctl_ip_default_ttl); -static int ip_fragment(struct sock *sk, struct sk_buff *skb, - unsigned int mtu, - int (*output)(struct sock *, struct sk_buff *)); +static int +ip_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, + unsigned int mtu, + int (*output)(struct net *, struct sock *, struct sk_buff *)); /* Generate a checksum for an outgoing IP datagram. */ void ip_send_check(struct iphdr *iph) @@ -176,12 +177,11 @@ int ip_build_and_send_pkt(struct sk_buff *skb, const struct sock *sk, } EXPORT_SYMBOL_GPL(ip_build_and_send_pkt); -static int ip_finish_output2(struct sock *sk, struct sk_buff *skb) +static int ip_finish_output2(struct net *net, struct sock *sk, struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); struct rtable *rt = (struct rtable *)dst; struct net_device *dev = dst->dev; - struct net *net = dev_net(dev); unsigned int hh_len = LL_RESERVED_SPACE(dev); struct neighbour *neigh; u32 nexthop; @@ -225,8 +225,8 @@ static int ip_finish_output2(struct sock *sk, struct sk_buff *skb) return -EINVAL; } -static int ip_finish_output_gso(struct sock *sk, struct sk_buff *skb, - unsigned int mtu) +static int ip_finish_output_gso(struct net *net, struct sock *sk, + struct sk_buff *skb, unsigned int mtu) { netdev_features_t features; struct sk_buff *segs; @@ -235,7 +235,7 @@ static int ip_finish_output_gso(struct sock *sk, struct sk_buff *skb, /* common case: locally created skb or seglen is <= mtu */ if (((IPCB(skb)->flags & IPSKB_FORWARDED) == 0) || skb_gso_network_seglen(skb) <= mtu) - return ip_finish_output2(sk, skb); + return ip_finish_output2(net, sk, skb); /* Slowpath - GSO segment length is exceeding the dst MTU. * @@ -258,7 +258,7 @@ static int ip_finish_output_gso(struct sock *sk, struct sk_buff *skb, int err; segs->next = NULL; - err = ip_fragment(sk, segs, mtu, ip_finish_output2); + err = ip_fragment(net, sk, segs, mtu, ip_finish_output2); if (err && ret == 0) ret = err; @@ -281,12 +281,12 @@ static int ip_finish_output(struct net *net, struct sock *sk, struct sk_buff *sk #endif mtu = ip_skb_dst_mtu(skb); if (skb_is_gso(skb)) - return ip_finish_output_gso(sk, skb, mtu); + return ip_finish_output_gso(net, sk, skb, mtu); if (skb->len > mtu || (IPCB(skb)->flags & IPSKB_FRAG_PMTU)) - return ip_fragment(sk, skb, mtu, ip_finish_output2); + return ip_fragment(net, sk, skb, mtu, ip_finish_output2); - return ip_finish_output2(sk, skb); + return ip_finish_output2(net, sk, skb); } int ip_mc_output(struct sock *sk, struct sk_buff *skb) @@ -495,20 +495,18 @@ static void ip_copy_metadata(struct sk_buff *to, struct sk_buff *from) skb_copy_secmark(to, from); } -static int ip_fragment(struct sock *sk, struct sk_buff *skb, +static int ip_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, unsigned int mtu, - int (*output)(struct sock *, struct sk_buff *)) + int (*output)(struct net *, struct sock *, struct sk_buff *)) { struct iphdr *iph = ip_hdr(skb); if ((iph->frag_off & htons(IP_DF)) == 0) - return ip_do_fragment(sk, skb, output); + return ip_do_fragment(net, sk, skb, output); if (unlikely(!skb->ignore_df || (IPCB(skb)->frag_max_size && IPCB(skb)->frag_max_size > mtu))) { - struct net *net = dev_net(skb_rtable(skb)->dst.dev); - IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS); icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu)); @@ -516,7 +514,7 @@ static int ip_fragment(struct sock *sk, struct sk_buff *skb, return -EMSGSIZE; } - return ip_do_fragment(sk, skb, output); + return ip_do_fragment(net, sk, skb, output); } /* @@ -526,8 +524,8 @@ static int ip_fragment(struct sock *sk, struct sk_buff *skb, * single device frame, and queue such a frame for sending. */ -int ip_do_fragment(struct sock *sk, struct sk_buff *skb, - int (*output)(struct sock *, struct sk_buff *)) +int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, + int (*output)(struct net *, struct sock *, struct sk_buff *)) { struct iphdr *iph; int ptr; @@ -537,11 +535,9 @@ int ip_do_fragment(struct sock *sk, struct sk_buff *skb, int offset; __be16 not_last_frag; struct rtable *rt = skb_rtable(skb); - struct net *net; int err = 0; dev = rt->dst.dev; - net = dev_net(dev); /* * Point into the IP datagram header. @@ -631,7 +627,7 @@ int ip_do_fragment(struct sock *sk, struct sk_buff *skb, ip_send_check(iph); } - err = output(sk, skb); + err = output(net, sk, skb); if (!err) IP_INC_STATS(net, IPSTATS_MIB_FRAGCREATES); @@ -771,7 +767,7 @@ slow_path: ip_send_check(iph); - err = output(sk, skb2); + err = output(net, sk, skb2); if (err) goto fail; diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index ba38662..b281b2b 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -705,7 +705,7 @@ static void ovs_fragment(struct net *net, struct vport *vport, skb_dst_set_noref(skb, &ovs_dst); IPCB(skb)->frag_max_size = mru; - ip_do_fragment(skb->sk, skb, ovs_vport_output_sk); + ip_do_fragment(net, skb->sk, skb, ovs_vport_output); refdst_drop(orig_dst); } else if (ethertype == htons(ETH_P_IPV6)) { const struct nf_ipv6_ops *v6ops = nf_get_ipv6_ops(); -- cgit v0.10.2 From 7d8c6e391575ee86c870b88635a163743fca9eac Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 12 Jun 2015 22:12:04 -0500 Subject: ipv6: Pass struct net through ip6_fragment Signed-off-by: Eric W. Biederman diff --git a/include/linux/netfilter_ipv6.h b/include/linux/netfilter_ipv6.h index 2ac8369..47c6b04 100644 --- a/include/linux/netfilter_ipv6.h +++ b/include/linux/netfilter_ipv6.h @@ -17,8 +17,8 @@ struct nf_ipv6_ops { int (*chk_addr)(struct net *net, const struct in6_addr *addr, const struct net_device *dev, int strict); void (*route_input)(struct sk_buff *skb); - int (*fragment)(struct sock *sk, struct sk_buff *skb, - int (*output)(struct sock *, struct sk_buff *)); + int (*fragment)(struct net *net, struct sock *sk, struct sk_buff *skb, + int (*output)(struct net *, struct sock *, struct sk_buff *)); }; #ifdef CONFIG_NETFILTER diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 297629a..2bfb2ad 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -173,8 +173,8 @@ static inline bool ipv6_anycast_destination(const struct dst_entry *dst, ipv6_addr_equal(&rt->rt6i_dst.addr, daddr)); } -int ip6_fragment(struct sock *sk, struct sk_buff *skb, - int (*output)(struct sock *, struct sk_buff *)); +int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, + int (*output)(struct net *, struct sock *, struct sk_buff *)); static inline int ip6_skb_dst_mtu(struct sk_buff *skb) { diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c index 00e356c..815994d 100644 --- a/net/bridge/br_netfilter_hooks.c +++ b/net/bridge/br_netfilter_hooks.c @@ -786,7 +786,7 @@ static int br_nf_dev_queue_xmit(struct net *net, struct sock *sk, struct sk_buff data->size); if (v6ops) - return v6ops->fragment(sk, skb, br_nf_push_frag_xmit_sk); + return v6ops->fragment(net, sk, skb, br_nf_push_frag_xmit); kfree_skb(skb); return -EMSGSIZE; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index a598fe2..caf7d14 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -56,11 +56,10 @@ #include #include -static int ip6_finish_output2(struct sock *sk, struct sk_buff *skb) +static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); struct net_device *dev = dst->dev; - struct net *net = dev_net(dev); struct neighbour *neigh; struct in6_addr *nexthop; int ret; @@ -126,9 +125,9 @@ static int ip6_finish_output(struct net *net, struct sock *sk, struct sk_buff *s 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)) - return ip6_fragment(sk, skb, ip6_finish_output2); + return ip6_fragment(net, sk, skb, ip6_finish_output2); else - return ip6_finish_output2(sk, skb); + return ip6_finish_output2(net, sk, skb); } int ip6_output(struct sock *sk, struct sk_buff *skb) @@ -554,8 +553,8 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from) skb_copy_secmark(to, from); } -int ip6_fragment(struct sock *sk, struct sk_buff *skb, - int (*output)(struct sock *, struct sk_buff *)) +int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, + int (*output)(struct net *, struct sock *, struct sk_buff *)) { struct sk_buff *frag; struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); @@ -568,7 +567,6 @@ int ip6_fragment(struct sock *sk, struct sk_buff *skb, __be32 frag_id; int ptr, offset = 0, err = 0; u8 *prevhdr, nexthdr = 0; - struct net *net = dev_net(skb_dst(skb)->dev); hlen = ip6_find_1stfragopt(skb, &prevhdr); nexthdr = *prevhdr; @@ -688,7 +686,7 @@ int ip6_fragment(struct sock *sk, struct sk_buff *skb, ip6_copy_metadata(frag, skb); } - err = output(sk, skb); + err = output(net, sk, skb); if (!err) IP6_INC_STATS(net, ip6_dst_idev(&rt->dst), IPSTATS_MIB_FRAGCREATES); @@ -816,7 +814,7 @@ slow_path: /* * Put this fragment into the sending queue. */ - err = output(sk, frag); + err = output(net, sk, frag); if (err) goto fail; diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c index 0c3e9ff..335066a 100644 --- a/net/ipv6/xfrm6_output.c +++ b/net/ipv6/xfrm6_output.c @@ -131,6 +131,12 @@ int xfrm6_output_finish(struct sock *sk, struct sk_buff *skb) return xfrm_output(sk, skb); } +static int __xfrm6_output_finish(struct net *net, struct sock *sk, struct sk_buff *skb) +{ + struct xfrm_state *x = skb_dst(skb)->xfrm; + return x->outer_mode->afinfo->output_finish(sk, skb); +} + static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); @@ -160,8 +166,8 @@ static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb) if (x->props.mode == XFRM_MODE_TUNNEL && ((skb->len > mtu && !skb_is_gso(skb)) || dst_allfrag(skb_dst(skb)))) { - return ip6_fragment(sk, skb, - x->outer_mode->afinfo->output_finish); + return ip6_fragment(net, sk, skb, + __xfrm6_output_finish); } return x->outer_mode->afinfo->output_finish(sk, skb); } diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index b281b2b..f33c627 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -727,7 +727,7 @@ static void ovs_fragment(struct net *net, struct vport *vport, skb_dst_set_noref(skb, &ovs_rt.dst); IP6CB(skb)->frag_max_size = mru; - v6ops->fragment(skb->sk, skb, ovs_vport_output_sk); + v6ops->fragment(net, skb->sk, skb, ovs_vport_output); refdst_drop(orig_dst); } else { WARN_ONCE(1, "Failed fragment ->%s: eth=%04x, MRU=%d, MTU=%d.", -- cgit v0.10.2 From 75aec9df3a7895747a0d022b7c83a1dfb2adf942 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 14 Sep 2015 13:46:16 -0500 Subject: bridge: Remove br_nf_push_frag_xmit_sk Now that this compatability function no longer has any callers remove it. Signed-off-by: "Eric W. Biederman" diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c index 815994d..370aa4d 100644 --- a/net/bridge/br_netfilter_hooks.c +++ b/net/bridge/br_netfilter_hooks.c @@ -691,11 +691,6 @@ static int br_nf_push_frag_xmit(struct net *net, struct sock *sk, struct sk_buff nf_bridge_info_free(skb); return br_dev_queue_push_xmit(net, sk, skb); } -static int br_nf_push_frag_xmit_sk(struct sock *sk, struct sk_buff *skb) -{ - struct net *net = dev_net(skb_dst(skb)->dev); - return br_nf_push_frag_xmit(net, sk, skb); -} #endif #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) -- cgit v0.10.2 From 184e16d79d38634cbb7b7c1cd3832caf89595c9a Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 14 Sep 2015 20:14:45 -0500 Subject: openvswitch: Remove ovs_vport_output_sk This was a compatibility function needed while the ipv4 and ipv6 fragmentation code was being modified to pass a struct net through them. Now that is complete this function has no more users so remove it. Signed-off-by: "Eric W. Biederman" diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index f33c627..1d21ab9 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -645,11 +645,6 @@ static int ovs_vport_output(struct net *net, struct sock *sk, struct sk_buff *sk ovs_vport_send(vport, skb); return 0; } -static int ovs_vport_output_sk(struct sock *sk, struct sk_buff *skb) -{ - struct net *net = dev_net(skb_dst(skb)->dev); - return ovs_vport_output(net, sk, skb); -} static unsigned int ovs_dst_get_mtu(const struct dst_entry *dst) -- cgit v0.10.2 From c648a0138b8f79b6cb4bd092dfab761e4becb1c2 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 28 Sep 2015 09:00:23 +0200 Subject: netlink: add nla_get for le32 and le64 This patch adds missing inline wrappers for nla_get_le32 and nla_get_le64. The 802.15.4 MAC byteorder is little endian and we keep the byteorder for fields like address configuration in the same byteorder as it comes from the MAC layer. To provide these fields for nl802154 userspace applications, we need these inline wrappers for netlink. Cc: David S. Miller Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/include/net/netlink.h b/include/net/netlink.h index 2a5dbcc..0e31727 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h @@ -1004,6 +1004,15 @@ static inline __be32 nla_get_be32(const struct nlattr *nla) } /** + * nla_get_le32 - return payload of __le32 attribute + * @nla: __le32 netlink attribute + */ +static inline __le32 nla_get_le32(const struct nlattr *nla) +{ + return *(__le32 *) nla_data(nla); +} + +/** * nla_get_u16 - return payload of u16 attribute * @nla: u16 netlink attribute */ @@ -1066,6 +1075,15 @@ static inline __be64 nla_get_be64(const struct nlattr *nla) } /** + * nla_get_le64 - return payload of __le64 attribute + * @nla: __le64 netlink attribute + */ +static inline __le64 nla_get_le64(const struct nlattr *nla) +{ + return *(__le64 *) nla_data(nla); +} + +/** * nla_get_s32 - return payload of s32 attribute * @nla: s32 netlink attribute */ -- cgit v0.10.2 From 1ee06ef1596dcc5858ea29ef9faf0f29e139dfcc Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 28 Sep 2015 09:00:24 +0200 Subject: nl802154: use nla_get_le64 for get extended addr This patch uses the nla_get_le64 function instead of doing a force converting to le64. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c index 3f89c0a..51110a6 100644 --- a/net/ieee802154/nl802154.c +++ b/net/ieee802154/nl802154.c @@ -753,10 +753,8 @@ static int nl802154_new_interface(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } - /* TODO add nla_get_le64 to netlink */ if (info->attrs[NL802154_ATTR_EXTENDED_ADDR]) - extended_addr = (__force __le64)nla_get_u64( - info->attrs[NL802154_ATTR_EXTENDED_ADDR]); + extended_addr = nla_get_le64(info->attrs[NL802154_ATTR_EXTENDED_ADDR]); if (!rdev->ops->add_virtual_intf) return -EOPNOTSUPP; -- cgit v0.10.2 From a26c5fd7622d4951425131d54a8c99f076fe2068 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 28 Sep 2015 09:00:25 +0200 Subject: nl802154: add support for security layer This patch adds support for accessing mac802154 llsec implementation over nl802154. I added for a new Kconfig entry to provide this functionality CONFIG_IEEE802154_NL802154_EXPERIMENTAL. This interface is still in development. It provides to change security parameters and add/del/dump entries of security tables. Later we can add also a get to get an entry by unique identifier. Cc: Phoebe Buckheister Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h index 242273c..171cd76 100644 --- a/include/net/cfg802154.h +++ b/include/net/cfg802154.h @@ -27,6 +27,16 @@ struct wpan_phy; struct wpan_phy_cca; +#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL +struct ieee802154_llsec_device_key; +struct ieee802154_llsec_seclevel; +struct ieee802154_llsec_params; +struct ieee802154_llsec_device; +struct ieee802154_llsec_table; +struct ieee802154_llsec_key_id; +struct ieee802154_llsec_key; +#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ + struct cfg802154_ops { struct net_device * (*add_virtual_intf_deprecated)(struct wpan_phy *wpan_phy, const char *name, @@ -65,6 +75,51 @@ struct cfg802154_ops { struct wpan_dev *wpan_dev, bool mode); int (*set_ackreq_default)(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, bool ackreq); +#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL + void (*get_llsec_table)(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev, + struct ieee802154_llsec_table **table); + void (*lock_llsec_table)(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev); + void (*unlock_llsec_table)(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev); + /* TODO remove locking/get table callbacks, this is part of the + * nl802154 interface and should be accessible from ieee802154 layer. + */ + int (*get_llsec_params)(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev, + struct ieee802154_llsec_params *params); + int (*set_llsec_params)(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev, + const struct ieee802154_llsec_params *params, + int changed); + int (*add_llsec_key)(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev, + const struct ieee802154_llsec_key_id *id, + const struct ieee802154_llsec_key *key); + int (*del_llsec_key)(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev, + const struct ieee802154_llsec_key_id *id); + int (*add_seclevel)(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev, + const struct ieee802154_llsec_seclevel *sl); + int (*del_seclevel)(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev, + const struct ieee802154_llsec_seclevel *sl); + int (*add_device)(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev, + const struct ieee802154_llsec_device *dev); + int (*del_device)(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev, __le64 extended_addr); + int (*add_devkey)(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev, + __le64 extended_addr, + const struct ieee802154_llsec_device_key *key); + int (*del_devkey)(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev, + __le64 extended_addr, + const struct ieee802154_llsec_device_key *key); +#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ }; static inline bool @@ -176,6 +231,82 @@ struct ieee802154_addr { }; }; +struct ieee802154_llsec_key_id { + u8 mode; + u8 id; + union { + struct ieee802154_addr device_addr; + __le32 short_source; + __le64 extended_source; + }; +}; + +#define IEEE802154_LLSEC_KEY_SIZE 16 + +struct ieee802154_llsec_key { + u8 frame_types; + u32 cmd_frame_ids; + /* TODO replace with NL802154_KEY_SIZE */ + u8 key[IEEE802154_LLSEC_KEY_SIZE]; +}; + +struct ieee802154_llsec_key_entry { + struct list_head list; + + struct ieee802154_llsec_key_id id; + struct ieee802154_llsec_key *key; +}; + +struct ieee802154_llsec_params { + bool enabled; + + __be32 frame_counter; + u8 out_level; + struct ieee802154_llsec_key_id out_key; + + __le64 default_key_source; + + __le16 pan_id; + __le64 hwaddr; + __le64 coord_hwaddr; + __le16 coord_shortaddr; +}; + +struct ieee802154_llsec_table { + struct list_head keys; + struct list_head devices; + struct list_head security_levels; +}; + +struct ieee802154_llsec_seclevel { + struct list_head list; + + u8 frame_type; + u8 cmd_frame_id; + bool device_override; + u32 sec_levels; +}; + +struct ieee802154_llsec_device { + struct list_head list; + + __le16 pan_id; + __le16 short_addr; + __le64 hwaddr; + u32 frame_counter; + bool seclevel_exempt; + + u8 key_mode; + struct list_head keys; +}; + +struct ieee802154_llsec_device_key { + struct list_head list; + + struct ieee802154_llsec_key_id key_id; + u32 frame_counter; +}; + struct wpan_dev_header_ops { /* TODO create callback currently assumes ieee802154_mac_cb inside * skb->cb. This should be changed to give these information as diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h index aebb9d8..a62a051 100644 --- a/include/net/ieee802154_netdev.h +++ b/include/net/ieee802154_netdev.h @@ -234,38 +234,6 @@ static inline struct ieee802154_mac_cb *mac_cb_init(struct sk_buff *skb) return mac_cb(skb); } -#define IEEE802154_LLSEC_KEY_SIZE 16 - -struct ieee802154_llsec_key_id { - u8 mode; - u8 id; - union { - struct ieee802154_addr device_addr; - __le32 short_source; - __le64 extended_source; - }; -}; - -struct ieee802154_llsec_key { - u8 frame_types; - u32 cmd_frame_ids; - u8 key[IEEE802154_LLSEC_KEY_SIZE]; -}; - -struct ieee802154_llsec_key_entry { - struct list_head list; - - struct ieee802154_llsec_key_id id; - struct ieee802154_llsec_key *key; -}; - -struct ieee802154_llsec_device_key { - struct list_head list; - - struct ieee802154_llsec_key_id key_id; - u32 frame_counter; -}; - enum { IEEE802154_LLSEC_DEVKEY_IGNORE, IEEE802154_LLSEC_DEVKEY_RESTRICT, @@ -274,49 +242,6 @@ enum { __IEEE802154_LLSEC_DEVKEY_MAX, }; -struct ieee802154_llsec_device { - struct list_head list; - - __le16 pan_id; - __le16 short_addr; - __le64 hwaddr; - u32 frame_counter; - bool seclevel_exempt; - - u8 key_mode; - struct list_head keys; -}; - -struct ieee802154_llsec_seclevel { - struct list_head list; - - u8 frame_type; - u8 cmd_frame_id; - bool device_override; - u32 sec_levels; -}; - -struct ieee802154_llsec_params { - bool enabled; - - __be32 frame_counter; - u8 out_level; - struct ieee802154_llsec_key_id out_key; - - __le64 default_key_source; - - __le16 pan_id; - __le64 hwaddr; - __le64 coord_hwaddr; - __le16 coord_shortaddr; -}; - -struct ieee802154_llsec_table { - struct list_head keys; - struct list_head devices; - struct list_head security_levels; -}; - #define IEEE802154_MAC_SCAN_ED 0 #define IEEE802154_MAC_SCAN_ACTIVE 1 #define IEEE802154_MAC_SCAN_PASSIVE 2 diff --git a/include/net/nl802154.h b/include/net/nl802154.h index cf2713d..32cb3e5 100644 --- a/include/net/nl802154.h +++ b/include/net/nl802154.h @@ -56,6 +56,22 @@ enum nl802154_commands { /* add new commands above here */ +#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL + NL802154_CMD_SET_SEC_PARAMS, + NL802154_CMD_GET_SEC_KEY, /* can dump */ + NL802154_CMD_NEW_SEC_KEY, + NL802154_CMD_DEL_SEC_KEY, + NL802154_CMD_GET_SEC_DEV, /* can dump */ + NL802154_CMD_NEW_SEC_DEV, + NL802154_CMD_DEL_SEC_DEV, + NL802154_CMD_GET_SEC_DEVKEY, /* can dump */ + NL802154_CMD_NEW_SEC_DEVKEY, + NL802154_CMD_DEL_SEC_DEVKEY, + NL802154_CMD_GET_SEC_LEVEL, /* can dump */ + NL802154_CMD_NEW_SEC_LEVEL, + NL802154_CMD_DEL_SEC_LEVEL, +#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ + /* used to define NL802154_CMD_MAX below */ __NL802154_CMD_AFTER_LAST, NL802154_CMD_MAX = __NL802154_CMD_AFTER_LAST - 1 @@ -110,6 +126,18 @@ enum nl802154_attrs { /* add attributes here, update the policy in nl802154.c */ +#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL + NL802154_ATTR_SEC_ENABLED, + NL802154_ATTR_SEC_OUT_LEVEL, + NL802154_ATTR_SEC_OUT_KEY_ID, + NL802154_ATTR_SEC_FRAME_COUNTER, + + NL802154_ATTR_SEC_LEVEL, + NL802154_ATTR_SEC_DEVICE, + NL802154_ATTR_SEC_DEVKEY, + NL802154_ATTR_SEC_KEY, +#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ + __NL802154_ATTR_AFTER_LAST, NL802154_ATTR_MAX = __NL802154_ATTR_AFTER_LAST - 1 }; @@ -247,4 +275,167 @@ enum nl802154_supported_bool_states { NL802154_SUPPORTED_BOOL_MAX = __NL802154_SUPPORTED_BOOL_AFTER_LAST - 1 }; +#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL + +enum nl802154_dev_addr_modes { + NL802154_DEV_ADDR_NONE, + __NL802154_DEV_ADDR_INVALID, + NL802154_DEV_ADDR_SHORT, + NL802154_DEV_ADDR_EXTENDED, + + /* keep last */ + __NL802154_DEV_ADDR_AFTER_LAST, + NL802154_DEV_ADDR_MAX = __NL802154_DEV_ADDR_AFTER_LAST - 1 +}; + +enum nl802154_dev_addr_attrs { + NL802154_DEV_ADDR_ATTR_UNSPEC, + + NL802154_DEV_ADDR_ATTR_PAN_ID, + NL802154_DEV_ADDR_ATTR_MODE, + NL802154_DEV_ADDR_ATTR_SHORT, + NL802154_DEV_ADDR_ATTR_EXTENDED, + + /* keep last */ + __NL802154_DEV_ADDR_ATTR_AFTER_LAST, + NL802154_DEV_ADDR_ATTR_MAX = __NL802154_DEV_ADDR_ATTR_AFTER_LAST - 1 +}; + +enum nl802154_key_id_modes { + NL802154_KEY_ID_MODE_IMPLICIT, + NL802154_KEY_ID_MODE_INDEX, + NL802154_KEY_ID_MODE_INDEX_SHORT, + NL802154_KEY_ID_MODE_INDEX_EXTENDED, + + /* keep last */ + __NL802154_KEY_ID_MODE_AFTER_LAST, + NL802154_KEY_ID_MODE_MAX = __NL802154_KEY_ID_MODE_AFTER_LAST - 1 +}; + +enum nl802154_key_id_attrs { + NL802154_KEY_ID_ATTR_UNSPEC, + + NL802154_KEY_ID_ATTR_MODE, + NL802154_KEY_ID_ATTR_INDEX, + NL802154_KEY_ID_ATTR_IMPLICIT, + NL802154_KEY_ID_ATTR_SOURCE_SHORT, + NL802154_KEY_ID_ATTR_SOURCE_EXTENDED, + + /* keep last */ + __NL802154_KEY_ID_ATTR_AFTER_LAST, + NL802154_KEY_ID_ATTR_MAX = __NL802154_KEY_ID_ATTR_AFTER_LAST - 1 +}; + +enum nl802154_seclevels { + NL802154_SECLEVEL_NONE, + NL802154_SECLEVEL_MIC32, + NL802154_SECLEVEL_MIC64, + NL802154_SECLEVEL_MIC128, + NL802154_SECLEVEL_ENC, + NL802154_SECLEVEL_ENC_MIC32, + NL802154_SECLEVEL_ENC_MIC64, + NL802154_SECLEVEL_ENC_MIC128, + + /* keep last */ + __NL802154_SECLEVEL_AFTER_LAST, + NL802154_SECLEVEL_MAX = __NL802154_SECLEVEL_AFTER_LAST - 1 +}; + +enum nl802154_frames { + NL802154_FRAME_BEACON, + NL802154_FRAME_DATA, + NL802154_FRAME_ACK, + NL802154_FRAME_CMD, + + /* keep last */ + __NL802154_FRAME_AFTER_LAST, + NL802154_FRAME_MAX = __NL802154_FRAME_AFTER_LAST - 1 +}; + +enum nl802154_cmd_frames { + __NL802154_CMD_FRAME_INVALID, + NL802154_CMD_FRAME_ASSOC_REQUEST, + NL802154_CMD_FRAME_ASSOC_RESPONSE, + NL802154_CMD_FRAME_DISASSOC_NOTIFY, + NL802154_CMD_FRAME_DATA_REQUEST, + NL802154_CMD_FRAME_PAN_ID_CONFLICT_NOTIFY, + NL802154_CMD_FRAME_ORPHAN_NOTIFY, + NL802154_CMD_FRAME_BEACON_REQUEST, + NL802154_CMD_FRAME_COORD_REALIGNMENT, + NL802154_CMD_FRAME_GTS_REQUEST, + + /* keep last */ + __NL802154_CMD_FRAME_AFTER_LAST, + NL802154_CMD_FRAME_MAX = __NL802154_CMD_FRAME_AFTER_LAST - 1 +}; + +enum nl802154_seclevel_attrs { + NL802154_SECLEVEL_ATTR_UNSPEC, + + NL802154_SECLEVEL_ATTR_LEVELS, + NL802154_SECLEVEL_ATTR_FRAME, + NL802154_SECLEVEL_ATTR_CMD_FRAME, + NL802154_SECLEVEL_ATTR_DEV_OVERRIDE, + + /* keep last */ + __NL802154_SECLEVEL_ATTR_AFTER_LAST, + NL802154_SECLEVEL_ATTR_MAX = __NL802154_SECLEVEL_ATTR_AFTER_LAST - 1 +}; + +/* TODO what is this? couldn't find in mib */ +enum { + NL802154_DEVKEY_IGNORE, + NL802154_DEVKEY_RESTRICT, + NL802154_DEVKEY_RECORD, + + /* keep last */ + __NL802154_DEVKEY_AFTER_LAST, + NL802154_DEVKEY_MAX = __NL802154_DEVKEY_AFTER_LAST - 1 +}; + +enum nl802154_dev { + NL802154_DEV_ATTR_UNSPEC, + + NL802154_DEV_ATTR_FRAME_COUNTER, + NL802154_DEV_ATTR_PAN_ID, + NL802154_DEV_ATTR_SHORT_ADDR, + NL802154_DEV_ATTR_EXTENDED_ADDR, + NL802154_DEV_ATTR_SECLEVEL_EXEMPT, + NL802154_DEV_ATTR_KEY_MODE, + + /* keep last */ + __NL802154_DEV_ATTR_AFTER_LAST, + NL802154_DEV_ATTR_MAX = __NL802154_DEV_ATTR_AFTER_LAST - 1 +}; + +enum nl802154_devkey { + NL802154_DEVKEY_ATTR_UNSPEC, + + NL802154_DEVKEY_ATTR_FRAME_COUNTER, + NL802154_DEVKEY_ATTR_EXTENDED_ADDR, + NL802154_DEVKEY_ATTR_ID, + + /* keep last */ + __NL802154_DEVKEY_ATTR_AFTER_LAST, + NL802154_DEVKEY_ATTR_MAX = __NL802154_DEVKEY_ATTR_AFTER_LAST - 1 +}; + +enum nl802154_key { + NL802154_KEY_ATTR_UNSPEC, + + NL802154_KEY_ATTR_ID, + NL802154_KEY_ATTR_USAGE_FRAMES, + NL802154_KEY_ATTR_USAGE_CMDS, + NL802154_KEY_ATTR_BYTES, + + /* keep last */ + __NL802154_KEY_ATTR_AFTER_LAST, + NL802154_KEY_ATTR_MAX = __NL802154_KEY_ATTR_AFTER_LAST - 1 +}; + +#define NL802154_KEY_SIZE 16 +#define NL802154_CMD_FRAME_NR_IDS 256 + +#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ + #endif /* __NL802154_H */ diff --git a/net/ieee802154/Kconfig b/net/ieee802154/Kconfig index 1370d5b..188135b 100644 --- a/net/ieee802154/Kconfig +++ b/net/ieee802154/Kconfig @@ -12,6 +12,11 @@ menuconfig IEEE802154 if IEEE802154 +config IEEE802154_NL802154_EXPERIMENTAL + bool "IEEE 802.15.4 experimental netlink support" + ---help--- + Adds experimental netlink support for nl802154. + config IEEE802154_SOCKET tristate "IEEE 802.15.4 socket interface" default y diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c index b0248e9..c35fdfa 100644 --- a/net/ieee802154/core.c +++ b/net/ieee802154/core.c @@ -95,6 +95,18 @@ cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx) return result; } +struct wpan_phy *wpan_phy_idx_to_wpan_phy(int wpan_phy_idx) +{ + struct cfg802154_registered_device *rdev; + + ASSERT_RTNL(); + + rdev = cfg802154_rdev_by_wpan_phy_idx(wpan_phy_idx); + if (!rdev) + return NULL; + return &rdev->wpan_phy; +} + struct wpan_phy * wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size) { diff --git a/net/ieee802154/core.h b/net/ieee802154/core.h index f3e9558..231fade 100644 --- a/net/ieee802154/core.h +++ b/net/ieee802154/core.h @@ -42,5 +42,6 @@ extern int cfg802154_rdev_list_generation; void cfg802154_dev_free(struct cfg802154_registered_device *rdev); struct cfg802154_registered_device * cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx); +struct wpan_phy *wpan_phy_idx_to_wpan_phy(int wpan_phy_idx); #endif /* __IEEE802154_CORE_H */ diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c index 51110a6..1e9e865 100644 --- a/net/ieee802154/nl802154.c +++ b/net/ieee802154/nl802154.c @@ -232,8 +232,86 @@ static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = { [NL802154_ATTR_SUPPORTED_COMMANDS] = { .type = NLA_NESTED }, [NL802154_ATTR_ACKREQ_DEFAULT] = { .type = NLA_U8 }, + +#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL + [NL802154_ATTR_SEC_ENABLED] = { .type = NLA_U8, }, + [NL802154_ATTR_SEC_OUT_LEVEL] = { .type = NLA_U32, }, + [NL802154_ATTR_SEC_OUT_KEY_ID] = { .type = NLA_NESTED, }, + [NL802154_ATTR_SEC_FRAME_COUNTER] = { .type = NLA_U32 }, + + [NL802154_ATTR_SEC_LEVEL] = { .type = NLA_NESTED }, + [NL802154_ATTR_SEC_DEVICE] = { .type = NLA_NESTED }, + [NL802154_ATTR_SEC_DEVKEY] = { .type = NLA_NESTED }, + [NL802154_ATTR_SEC_KEY] = { .type = NLA_NESTED }, +#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ }; +#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL +static int +nl802154_prepare_wpan_dev_dump(struct sk_buff *skb, + struct netlink_callback *cb, + struct cfg802154_registered_device **rdev, + struct wpan_dev **wpan_dev) +{ + int err; + + rtnl_lock(); + + if (!cb->args[0]) { + err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl802154_fam.hdrsize, + nl802154_fam.attrbuf, nl802154_fam.maxattr, + nl802154_policy); + if (err) + goto out_unlock; + + *wpan_dev = __cfg802154_wpan_dev_from_attrs(sock_net(skb->sk), + nl802154_fam.attrbuf); + if (IS_ERR(*wpan_dev)) { + err = PTR_ERR(*wpan_dev); + goto out_unlock; + } + *rdev = wpan_phy_to_rdev((*wpan_dev)->wpan_phy); + /* 0 is the first index - add 1 to parse only once */ + cb->args[0] = (*rdev)->wpan_phy_idx + 1; + cb->args[1] = (*wpan_dev)->identifier; + } else { + /* subtract the 1 again here */ + struct wpan_phy *wpan_phy = wpan_phy_idx_to_wpan_phy(cb->args[0] - 1); + struct wpan_dev *tmp; + + if (!wpan_phy) { + err = -ENODEV; + goto out_unlock; + } + *rdev = wpan_phy_to_rdev(wpan_phy); + *wpan_dev = NULL; + + list_for_each_entry(tmp, &(*rdev)->wpan_dev_list, list) { + if (tmp->identifier == cb->args[1]) { + *wpan_dev = tmp; + break; + } + } + + if (!*wpan_dev) { + err = -ENODEV; + goto out_unlock; + } + } + + return 0; + out_unlock: + rtnl_unlock(); + return err; +} + +static void +nl802154_finish_wpan_dev_dump(struct cfg802154_registered_device *rdev) +{ + rtnl_unlock(); +} +#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ + /* message building helper */ static inline void *nl802154hdr_put(struct sk_buff *skb, u32 portid, u32 seq, int flags, u8 cmd) @@ -612,6 +690,107 @@ static inline u64 wpan_dev_id(struct wpan_dev *wpan_dev) ((u64)wpan_phy_to_rdev(wpan_dev->wpan_phy)->wpan_phy_idx << 32); } +#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL +#include + +static int +ieee802154_llsec_send_key_id(struct sk_buff *msg, + const struct ieee802154_llsec_key_id *desc) +{ + struct nlattr *nl_dev_addr; + + if (nla_put_u32(msg, NL802154_KEY_ID_ATTR_MODE, desc->mode)) + return -ENOBUFS; + + switch (desc->mode) { + case NL802154_KEY_ID_MODE_IMPLICIT: + nl_dev_addr = nla_nest_start(msg, NL802154_KEY_ID_ATTR_IMPLICIT); + if (!nl_dev_addr) + return -ENOBUFS; + + if (nla_put_le16(msg, NL802154_DEV_ADDR_ATTR_PAN_ID, + desc->device_addr.pan_id) || + nla_put_u32(msg, NL802154_DEV_ADDR_ATTR_MODE, + desc->device_addr.mode)) + return -ENOBUFS; + + switch (desc->device_addr.mode) { + case NL802154_DEV_ADDR_SHORT: + if (nla_put_le16(msg, NL802154_DEV_ADDR_ATTR_SHORT, + desc->device_addr.short_addr)) + return -ENOBUFS; + break; + case NL802154_DEV_ADDR_EXTENDED: + if (nla_put_le64(msg, NL802154_DEV_ADDR_ATTR_EXTENDED, + desc->device_addr.extended_addr)) + return -ENOBUFS; + break; + default: + /* userspace should handle unknown */ + break; + } + + nla_nest_end(msg, nl_dev_addr); + break; + case NL802154_KEY_ID_MODE_INDEX: + break; + case NL802154_KEY_ID_MODE_INDEX_SHORT: + /* TODO renmae short_source? */ + if (nla_put_le32(msg, NL802154_KEY_ID_ATTR_SOURCE_SHORT, + desc->short_source)) + return -ENOBUFS; + break; + case NL802154_KEY_ID_MODE_INDEX_EXTENDED: + if (nla_put_le64(msg, NL802154_KEY_ID_ATTR_SOURCE_EXTENDED, + desc->extended_source)) + return -ENOBUFS; + break; + default: + /* userspace should handle unknown */ + break; + } + + /* TODO key_id to key_idx ? Check naming */ + if (desc->mode != NL802154_KEY_ID_MODE_IMPLICIT) { + if (nla_put_u8(msg, NL802154_KEY_ID_ATTR_INDEX, desc->id)) + return -ENOBUFS; + } + + return 0; +} + +static int nl802154_get_llsec_params(struct sk_buff *msg, + struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev) +{ + struct nlattr *nl_key_id; + struct ieee802154_llsec_params params; + int ret; + + ret = rdev_get_llsec_params(rdev, wpan_dev, ¶ms); + if (ret < 0) + return ret; + + if (nla_put_u8(msg, NL802154_ATTR_SEC_ENABLED, params.enabled) || + nla_put_u32(msg, NL802154_ATTR_SEC_OUT_LEVEL, params.out_level) || + nla_put_be32(msg, NL802154_ATTR_SEC_FRAME_COUNTER, + params.frame_counter)) + return -ENOBUFS; + + nl_key_id = nla_nest_start(msg, NL802154_ATTR_SEC_OUT_KEY_ID); + if (!nl_key_id) + return -ENOBUFS; + + ret = ieee802154_llsec_send_key_id(msg, ¶ms.out_key); + if (ret < 0) + return ret; + + nla_nest_end(msg, nl_key_id); + + return 0; +} +#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ + static int nl802154_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags, struct cfg802154_registered_device *rdev, @@ -663,6 +842,11 @@ nl802154_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags, if (nla_put_u8(msg, NL802154_ATTR_ACKREQ_DEFAULT, wpan_dev->ackreq)) goto nla_put_failure; +#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL + if (nl802154_get_llsec_params(msg, rdev, wpan_dev) < 0) + goto nla_put_failure; +#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ + genlmsg_end(msg, hdr); return 0; @@ -1073,122 +1257,953 @@ nl802154_set_ackreq_default(struct sk_buff *skb, struct genl_info *info) return rdev_set_ackreq_default(rdev, wpan_dev, ackreq); } -#define NL802154_FLAG_NEED_WPAN_PHY 0x01 -#define NL802154_FLAG_NEED_NETDEV 0x02 -#define NL802154_FLAG_NEED_RTNL 0x04 -#define NL802154_FLAG_CHECK_NETDEV_UP 0x08 -#define NL802154_FLAG_NEED_NETDEV_UP (NL802154_FLAG_NEED_NETDEV |\ - NL802154_FLAG_CHECK_NETDEV_UP) -#define NL802154_FLAG_NEED_WPAN_DEV 0x10 -#define NL802154_FLAG_NEED_WPAN_DEV_UP (NL802154_FLAG_NEED_WPAN_DEV |\ - NL802154_FLAG_CHECK_NETDEV_UP) +#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL +static const struct nla_policy nl802154_dev_addr_policy[NL802154_DEV_ADDR_ATTR_MAX + 1] = { + [NL802154_DEV_ADDR_ATTR_PAN_ID] = { .type = NLA_U16 }, + [NL802154_DEV_ADDR_ATTR_MODE] = { .type = NLA_U32 }, + [NL802154_DEV_ADDR_ATTR_SHORT] = { .type = NLA_U16 }, + [NL802154_DEV_ADDR_ATTR_EXTENDED] = { .type = NLA_U64 }, +}; -static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, - struct genl_info *info) +static int +ieee802154_llsec_parse_dev_addr(struct nlattr *nla, + struct ieee802154_addr *addr) { - struct cfg802154_registered_device *rdev; - struct wpan_dev *wpan_dev; - struct net_device *dev; - bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL; + struct nlattr *attrs[NL802154_DEV_ADDR_ATTR_MAX + 1]; - if (rtnl) - rtnl_lock(); + if (!nla || nla_parse_nested(attrs, NL802154_DEV_ADDR_ATTR_MAX, nla, + nl802154_dev_addr_policy)) + return -EINVAL; - if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) { - rdev = cfg802154_get_dev_from_info(genl_info_net(info), info); - if (IS_ERR(rdev)) { - if (rtnl) - rtnl_unlock(); - return PTR_ERR(rdev); - } - info->user_ptr[0] = rdev; - } else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV || - ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) { - ASSERT_RTNL(); - wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info), - info->attrs); - if (IS_ERR(wpan_dev)) { - if (rtnl) - rtnl_unlock(); - return PTR_ERR(wpan_dev); - } + if (!attrs[NL802154_DEV_ADDR_ATTR_PAN_ID] && + !attrs[NL802154_DEV_ADDR_ATTR_MODE] && + !(attrs[NL802154_DEV_ADDR_ATTR_SHORT] || + attrs[NL802154_DEV_ADDR_ATTR_EXTENDED])) + return -EINVAL; - dev = wpan_dev->netdev; - rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy); + addr->pan_id = nla_get_le16(attrs[NL802154_DEV_ADDR_ATTR_PAN_ID]); + addr->mode = nla_get_u32(attrs[NL802154_DEV_ADDR_ATTR_MODE]); + switch (addr->mode) { + case NL802154_DEV_ADDR_SHORT: + addr->short_addr = nla_get_le16(attrs[NL802154_DEV_ADDR_ATTR_SHORT]); + break; + case NL802154_DEV_ADDR_EXTENDED: + addr->extended_addr = nla_get_le64(attrs[NL802154_DEV_ADDR_ATTR_EXTENDED]); + break; + default: + return -EINVAL; + } - if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) { - if (!dev) { - if (rtnl) - rtnl_unlock(); - return -EINVAL; - } + return 0; +} - info->user_ptr[1] = dev; - } else { - info->user_ptr[1] = wpan_dev; - } +static const struct nla_policy nl802154_key_id_policy[NL802154_KEY_ID_ATTR_MAX + 1] = { + [NL802154_KEY_ID_ATTR_MODE] = { .type = NLA_U32 }, + [NL802154_KEY_ID_ATTR_INDEX] = { .type = NLA_U8 }, + [NL802154_KEY_ID_ATTR_IMPLICIT] = { .type = NLA_NESTED }, + [NL802154_KEY_ID_ATTR_SOURCE_SHORT] = { .type = NLA_U32 }, + [NL802154_KEY_ID_ATTR_SOURCE_EXTENDED] = { .type = NLA_U64 }, +}; - if (dev) { - if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP && - !netif_running(dev)) { - if (rtnl) - rtnl_unlock(); - return -ENETDOWN; - } +static int +ieee802154_llsec_parse_key_id(struct nlattr *nla, + struct ieee802154_llsec_key_id *desc) +{ + struct nlattr *attrs[NL802154_KEY_ID_ATTR_MAX + 1]; - dev_hold(dev); - } + if (!nla || nla_parse_nested(attrs, NL802154_KEY_ID_ATTR_MAX, nla, + nl802154_key_id_policy)) + return -EINVAL; - info->user_ptr[0] = rdev; + if (!attrs[NL802154_KEY_ID_ATTR_MODE]) + return -EINVAL; + + desc->mode = nla_get_u32(attrs[NL802154_KEY_ID_ATTR_MODE]); + switch (desc->mode) { + case NL802154_KEY_ID_MODE_IMPLICIT: + if (!attrs[NL802154_KEY_ID_ATTR_IMPLICIT]) + return -EINVAL; + + if (ieee802154_llsec_parse_dev_addr(attrs[NL802154_KEY_ID_ATTR_IMPLICIT], + &desc->device_addr) < 0) + return -EINVAL; + break; + case NL802154_KEY_ID_MODE_INDEX: + break; + case NL802154_KEY_ID_MODE_INDEX_SHORT: + if (!attrs[NL802154_KEY_ID_ATTR_SOURCE_SHORT]) + return -EINVAL; + + desc->short_source = nla_get_le32(attrs[NL802154_KEY_ID_ATTR_SOURCE_SHORT]); + break; + case NL802154_KEY_ID_MODE_INDEX_EXTENDED: + if (!attrs[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED]) + return -EINVAL; + + desc->extended_source = nla_get_le64(attrs[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED]); + break; + default: + return -EINVAL; + } + + if (desc->mode != NL802154_KEY_ID_MODE_IMPLICIT) { + if (!attrs[NL802154_KEY_ID_ATTR_INDEX]) + return -EINVAL; + + /* TODO change id to idx */ + desc->id = nla_get_u8(attrs[NL802154_KEY_ID_ATTR_INDEX]); } return 0; } -static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb, - struct genl_info *info) +static int nl802154_set_llsec_params(struct sk_buff *skb, + struct genl_info *info) { - if (info->user_ptr[1]) { - if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) { - struct wpan_dev *wpan_dev = info->user_ptr[1]; + struct cfg802154_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wpan_dev *wpan_dev = dev->ieee802154_ptr; + struct ieee802154_llsec_params params; + u32 changed = 0; + int ret; - if (wpan_dev->netdev) - dev_put(wpan_dev->netdev); - } else { - dev_put(info->user_ptr[1]); + if (info->attrs[NL802154_ATTR_SEC_ENABLED]) { + u8 enabled; + + enabled = nla_get_u8(info->attrs[NL802154_ATTR_SEC_ENABLED]); + if (enabled != 0 && enabled != 1) + return -EINVAL; + + params.enabled = nla_get_u8(info->attrs[NL802154_ATTR_SEC_ENABLED]); + changed |= IEEE802154_LLSEC_PARAM_ENABLED; + } + + if (info->attrs[NL802154_ATTR_SEC_OUT_KEY_ID]) { + ret = ieee802154_llsec_parse_key_id(info->attrs[NL802154_ATTR_SEC_OUT_KEY_ID], + ¶ms.out_key); + if (ret < 0) + return ret; + + changed |= IEEE802154_LLSEC_PARAM_OUT_KEY; + } + + if (info->attrs[NL802154_ATTR_SEC_OUT_LEVEL]) { + params.out_level = nla_get_u32(info->attrs[NL802154_ATTR_SEC_OUT_LEVEL]); + if (params.out_level > NL802154_SECLEVEL_MAX) + return -EINVAL; + + changed |= IEEE802154_LLSEC_PARAM_OUT_LEVEL; + } + + if (info->attrs[NL802154_ATTR_SEC_FRAME_COUNTER]) { + params.frame_counter = nla_get_be32(info->attrs[NL802154_ATTR_SEC_FRAME_COUNTER]); + changed |= IEEE802154_LLSEC_PARAM_FRAME_COUNTER; + } + + return rdev_set_llsec_params(rdev, wpan_dev, ¶ms, changed); +} + +static int nl802154_send_key(struct sk_buff *msg, u32 cmd, u32 portid, + u32 seq, int flags, + struct cfg802154_registered_device *rdev, + struct net_device *dev, + const struct ieee802154_llsec_key_entry *key) +{ + void *hdr; + u32 commands[NL802154_CMD_FRAME_NR_IDS / 32]; + struct nlattr *nl_key, *nl_key_id; + + hdr = nl802154hdr_put(msg, portid, seq, flags, cmd); + if (!hdr) + return -1; + + if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex)) + goto nla_put_failure; + + nl_key = nla_nest_start(msg, NL802154_ATTR_SEC_KEY); + if (!nl_key) + goto nla_put_failure; + + nl_key_id = nla_nest_start(msg, NL802154_KEY_ATTR_ID); + if (!nl_key_id) + goto nla_put_failure; + + if (ieee802154_llsec_send_key_id(msg, &key->id) < 0) + goto nla_put_failure; + + nla_nest_end(msg, nl_key_id); + + if (nla_put_u8(msg, NL802154_KEY_ATTR_USAGE_FRAMES, + key->key->frame_types)) + goto nla_put_failure; + + if (key->key->frame_types & BIT(NL802154_FRAME_CMD)) { + /* TODO for each nested */ + memset(commands, 0, sizeof(commands)); + commands[7] = key->key->cmd_frame_ids; + if (nla_put(msg, NL802154_KEY_ATTR_USAGE_CMDS, + sizeof(commands), commands)) + goto nla_put_failure; + } + + if (nla_put(msg, NL802154_KEY_ATTR_BYTES, NL802154_KEY_SIZE, + key->key->key)) + goto nla_put_failure; + + nla_nest_end(msg, nl_key); + genlmsg_end(msg, hdr); + + return 0; + +nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +static int +nl802154_dump_llsec_key(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct cfg802154_registered_device *rdev = NULL; + struct ieee802154_llsec_key_entry *key; + struct ieee802154_llsec_table *table; + struct wpan_dev *wpan_dev; + int err; + + err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev); + if (err) + return err; + + if (!wpan_dev->netdev) { + err = -EINVAL; + goto out_err; + } + + rdev_lock_llsec_table(rdev, wpan_dev); + rdev_get_llsec_table(rdev, wpan_dev, &table); + + /* TODO make it like station dump */ + if (cb->args[2]) + goto out; + + list_for_each_entry(key, &table->keys, list) { + if (nl802154_send_key(skb, NL802154_CMD_NEW_SEC_KEY, + NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + rdev, wpan_dev->netdev, key) < 0) { + /* TODO */ + err = -EIO; + rdev_unlock_llsec_table(rdev, wpan_dev); + goto out_err; } } - if (ops->internal_flags & NL802154_FLAG_NEED_RTNL) - rtnl_unlock(); + cb->args[2] = 1; + +out: + rdev_unlock_llsec_table(rdev, wpan_dev); + err = skb->len; +out_err: + nl802154_finish_wpan_dev_dump(rdev); + + return err; } -static const struct genl_ops nl802154_ops[] = { - { - .cmd = NL802154_CMD_GET_WPAN_PHY, - .doit = nl802154_get_wpan_phy, - .dumpit = nl802154_dump_wpan_phy, - .done = nl802154_dump_wpan_phy_done, - .policy = nl802154_policy, - /* can be retrieved by unprivileged users */ - .internal_flags = NL802154_FLAG_NEED_WPAN_PHY | - NL802154_FLAG_NEED_RTNL, - }, - { - .cmd = NL802154_CMD_GET_INTERFACE, - .doit = nl802154_get_interface, - .dumpit = nl802154_dump_interface, - .policy = nl802154_policy, - /* can be retrieved by unprivileged users */ - .internal_flags = NL802154_FLAG_NEED_WPAN_DEV | - NL802154_FLAG_NEED_RTNL, - }, - { - .cmd = NL802154_CMD_NEW_INTERFACE, - .doit = nl802154_new_interface, - .policy = nl802154_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL802154_FLAG_NEED_WPAN_PHY | +static const struct nla_policy nl802154_key_policy[NL802154_KEY_ATTR_MAX + 1] = { + [NL802154_KEY_ATTR_ID] = { NLA_NESTED }, + /* TODO handle it as for_each_nested and NLA_FLAG? */ + [NL802154_KEY_ATTR_USAGE_FRAMES] = { NLA_U8 }, + /* TODO handle it as for_each_nested, not static array? */ + [NL802154_KEY_ATTR_USAGE_CMDS] = { .len = NL802154_CMD_FRAME_NR_IDS / 8 }, + [NL802154_KEY_ATTR_BYTES] = { .len = NL802154_KEY_SIZE }, +}; + +static int nl802154_add_llsec_key(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg802154_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wpan_dev *wpan_dev = dev->ieee802154_ptr; + struct nlattr *attrs[NL802154_KEY_ATTR_MAX + 1]; + struct ieee802154_llsec_key key = { }; + struct ieee802154_llsec_key_id id = { }; + u32 commands[NL802154_CMD_FRAME_NR_IDS / 32] = { }; + + if (nla_parse_nested(attrs, NL802154_KEY_ATTR_MAX, + info->attrs[NL802154_ATTR_SEC_KEY], + nl802154_key_policy)) + return -EINVAL; + + if (!attrs[NL802154_KEY_ATTR_USAGE_FRAMES] || + !attrs[NL802154_KEY_ATTR_BYTES]) + + if (ieee802154_llsec_parse_key_id(attrs[NL802154_KEY_ATTR_ID], &id) < 0) + return -ENOBUFS; + + key.frame_types = nla_get_u8(attrs[NL802154_KEY_ATTR_USAGE_FRAMES]); + if (key.frame_types > BIT(NL802154_FRAME_MAX) || + ((key.frame_types & BIT(NL802154_FRAME_CMD)) && + !attrs[NL802154_KEY_ATTR_USAGE_CMDS])) + return -EINVAL; + + if (attrs[NL802154_KEY_ATTR_USAGE_CMDS]) { + /* TODO for each nested */ + nla_memcpy(commands, attrs[NL802154_KEY_ATTR_USAGE_CMDS], + NL802154_CMD_FRAME_NR_IDS / 8); + + /* TODO understand the -EINVAL logic here? last condition */ + if (commands[0] || commands[1] || commands[2] || commands[3] || + commands[4] || commands[5] || commands[6] || + commands[7] > BIT(NL802154_CMD_FRAME_MAX)) + return -EINVAL; + + key.cmd_frame_ids = commands[7]; + } else { + key.cmd_frame_ids = 0; + } + + nla_memcpy(key.key, attrs[NL802154_KEY_ATTR_BYTES], NL802154_KEY_SIZE); + + if (ieee802154_llsec_parse_key_id(attrs[NL802154_KEY_ATTR_ID], &id) < 0) + return -ENOBUFS; + + return rdev_add_llsec_key(rdev, wpan_dev, &id, &key); +} + +static int nl802154_del_llsec_key(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg802154_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wpan_dev *wpan_dev = dev->ieee802154_ptr; + struct nlattr *attrs[NL802154_KEY_ATTR_MAX + 1]; + struct ieee802154_llsec_key_id id; + + if (nla_parse_nested(attrs, NL802154_KEY_ATTR_MAX, + info->attrs[NL802154_ATTR_SEC_KEY], + nl802154_key_policy)) + return -EINVAL; + + if (ieee802154_llsec_parse_key_id(attrs[NL802154_KEY_ATTR_ID], &id) < 0) + return -ENOBUFS; + + return rdev_del_llsec_key(rdev, wpan_dev, &id); +} + +static int nl802154_send_device(struct sk_buff *msg, u32 cmd, u32 portid, + u32 seq, int flags, + struct cfg802154_registered_device *rdev, + struct net_device *dev, + const struct ieee802154_llsec_device *dev_desc) +{ + void *hdr; + struct nlattr *nl_device; + + hdr = nl802154hdr_put(msg, portid, seq, flags, cmd); + if (!hdr) + return -1; + + if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex)) + goto nla_put_failure; + + nl_device = nla_nest_start(msg, NL802154_ATTR_SEC_DEVICE); + if (!nl_device) + goto nla_put_failure; + + if (nla_put_u32(msg, NL802154_DEV_ATTR_FRAME_COUNTER, + dev_desc->frame_counter) || + nla_put_le16(msg, NL802154_DEV_ATTR_PAN_ID, dev_desc->pan_id) || + nla_put_le16(msg, NL802154_DEV_ATTR_SHORT_ADDR, + dev_desc->short_addr) || + nla_put_le64(msg, NL802154_DEV_ATTR_EXTENDED_ADDR, + dev_desc->hwaddr) || + nla_put_u8(msg, NL802154_DEV_ATTR_SECLEVEL_EXEMPT, + dev_desc->seclevel_exempt) || + nla_put_u32(msg, NL802154_DEV_ATTR_KEY_MODE, dev_desc->key_mode)) + goto nla_put_failure; + + nla_nest_end(msg, nl_device); + genlmsg_end(msg, hdr); + + return 0; + +nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +static int +nl802154_dump_llsec_dev(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct cfg802154_registered_device *rdev = NULL; + struct ieee802154_llsec_device *dev; + struct ieee802154_llsec_table *table; + struct wpan_dev *wpan_dev; + int err; + + err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev); + if (err) + return err; + + if (!wpan_dev->netdev) { + err = -EINVAL; + goto out_err; + } + + rdev_lock_llsec_table(rdev, wpan_dev); + rdev_get_llsec_table(rdev, wpan_dev, &table); + + /* TODO make it like station dump */ + if (cb->args[2]) + goto out; + + list_for_each_entry(dev, &table->devices, list) { + if (nl802154_send_device(skb, NL802154_CMD_NEW_SEC_LEVEL, + NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + rdev, wpan_dev->netdev, dev) < 0) { + /* TODO */ + err = -EIO; + rdev_unlock_llsec_table(rdev, wpan_dev); + goto out_err; + } + } + + cb->args[2] = 1; + +out: + rdev_unlock_llsec_table(rdev, wpan_dev); + err = skb->len; +out_err: + nl802154_finish_wpan_dev_dump(rdev); + + return err; +} + +static const struct nla_policy nl802154_dev_policy[NL802154_DEV_ATTR_MAX + 1] = { + [NL802154_DEV_ATTR_FRAME_COUNTER] = { NLA_U32 }, + [NL802154_DEV_ATTR_PAN_ID] = { .type = NLA_U16 }, + [NL802154_DEV_ATTR_SHORT_ADDR] = { .type = NLA_U16 }, + [NL802154_DEV_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 }, + [NL802154_DEV_ATTR_SECLEVEL_EXEMPT] = { NLA_U8 }, + [NL802154_DEV_ATTR_KEY_MODE] = { NLA_U32 }, +}; + +static int +ieee802154_llsec_parse_device(struct nlattr *nla, + struct ieee802154_llsec_device *dev) +{ + struct nlattr *attrs[NL802154_DEV_ATTR_MAX + 1]; + + if (!nla || nla_parse_nested(attrs, NL802154_DEV_ATTR_MAX, nla, + nl802154_dev_policy)) + return -EINVAL; + + memset(dev, 0, sizeof(*dev)); + + if (!attrs[NL802154_DEV_ATTR_FRAME_COUNTER] || + !attrs[NL802154_DEV_ATTR_PAN_ID] || + !attrs[NL802154_DEV_ATTR_SHORT_ADDR] || + !attrs[NL802154_DEV_ATTR_EXTENDED_ADDR] || + !attrs[NL802154_DEV_ATTR_SECLEVEL_EXEMPT] || + !attrs[NL802154_DEV_ATTR_KEY_MODE]) + return -EINVAL; + + /* TODO be32 */ + dev->frame_counter = nla_get_u32(attrs[NL802154_DEV_ATTR_FRAME_COUNTER]); + dev->pan_id = nla_get_le16(attrs[NL802154_DEV_ATTR_PAN_ID]); + dev->short_addr = nla_get_le16(attrs[NL802154_DEV_ATTR_SHORT_ADDR]); + /* TODO rename hwaddr to extended_addr */ + dev->hwaddr = nla_get_le64(attrs[NL802154_DEV_ATTR_EXTENDED_ADDR]); + dev->seclevel_exempt = nla_get_u8(attrs[NL802154_DEV_ATTR_SECLEVEL_EXEMPT]); + dev->key_mode = nla_get_u32(attrs[NL802154_DEV_ATTR_KEY_MODE]); + + if (dev->key_mode > NL802154_DEVKEY_MAX || + (dev->seclevel_exempt != 0 && dev->seclevel_exempt != 1)) + return -EINVAL; + + return 0; +} + +static int nl802154_add_llsec_dev(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg802154_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wpan_dev *wpan_dev = dev->ieee802154_ptr; + struct ieee802154_llsec_device dev_desc; + + if (ieee802154_llsec_parse_device(info->attrs[NL802154_ATTR_SEC_DEVICE], + &dev_desc) < 0) + return -EINVAL; + + return rdev_add_device(rdev, wpan_dev, &dev_desc); +} + +static int nl802154_del_llsec_dev(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg802154_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wpan_dev *wpan_dev = dev->ieee802154_ptr; + struct nlattr *attrs[NL802154_DEV_ATTR_MAX + 1]; + __le64 extended_addr; + + if (nla_parse_nested(attrs, NL802154_DEV_ATTR_MAX, + info->attrs[NL802154_ATTR_SEC_DEVICE], + nl802154_dev_policy)) + return -EINVAL; + + if (!attrs[NL802154_DEV_ATTR_EXTENDED_ADDR]) + return -EINVAL; + + extended_addr = nla_get_le64(attrs[NL802154_DEV_ATTR_EXTENDED_ADDR]); + return rdev_del_device(rdev, wpan_dev, extended_addr); +} + +static int nl802154_send_devkey(struct sk_buff *msg, u32 cmd, u32 portid, + u32 seq, int flags, + struct cfg802154_registered_device *rdev, + struct net_device *dev, __le64 extended_addr, + const struct ieee802154_llsec_device_key *devkey) +{ + void *hdr; + struct nlattr *nl_devkey, *nl_key_id; + + hdr = nl802154hdr_put(msg, portid, seq, flags, cmd); + if (!hdr) + return -1; + + if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex)) + goto nla_put_failure; + + nl_devkey = nla_nest_start(msg, NL802154_ATTR_SEC_DEVKEY); + if (!nl_devkey) + goto nla_put_failure; + + if (nla_put_le64(msg, NL802154_DEVKEY_ATTR_EXTENDED_ADDR, + extended_addr) || + nla_put_u32(msg, NL802154_DEVKEY_ATTR_FRAME_COUNTER, + devkey->frame_counter)) + goto nla_put_failure; + + nl_key_id = nla_nest_start(msg, NL802154_DEVKEY_ATTR_ID); + if (!nl_key_id) + goto nla_put_failure; + + if (ieee802154_llsec_send_key_id(msg, &devkey->key_id) < 0) + goto nla_put_failure; + + nla_nest_end(msg, nl_key_id); + nla_nest_end(msg, nl_devkey); + genlmsg_end(msg, hdr); + + return 0; + +nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +static int +nl802154_dump_llsec_devkey(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct cfg802154_registered_device *rdev = NULL; + struct ieee802154_llsec_device_key *kpos; + struct ieee802154_llsec_device *dpos; + struct ieee802154_llsec_table *table; + struct wpan_dev *wpan_dev; + int err; + + err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev); + if (err) + return err; + + if (!wpan_dev->netdev) { + err = -EINVAL; + goto out_err; + } + + rdev_lock_llsec_table(rdev, wpan_dev); + rdev_get_llsec_table(rdev, wpan_dev, &table); + + /* TODO make it like station dump */ + if (cb->args[2]) + goto out; + + /* TODO look if remove devkey and do some nested attribute */ + list_for_each_entry(dpos, &table->devices, list) { + list_for_each_entry(kpos, &dpos->keys, list) { + if (nl802154_send_devkey(skb, + NL802154_CMD_NEW_SEC_LEVEL, + NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, + NLM_F_MULTI, rdev, + wpan_dev->netdev, + dpos->hwaddr, + kpos) < 0) { + /* TODO */ + err = -EIO; + rdev_unlock_llsec_table(rdev, wpan_dev); + goto out_err; + } + } + } + + cb->args[2] = 1; + +out: + rdev_unlock_llsec_table(rdev, wpan_dev); + err = skb->len; +out_err: + nl802154_finish_wpan_dev_dump(rdev); + + return err; +} + +static const struct nla_policy nl802154_devkey_policy[NL802154_DEVKEY_ATTR_MAX + 1] = { + [NL802154_DEVKEY_ATTR_FRAME_COUNTER] = { NLA_U32 }, + [NL802154_DEVKEY_ATTR_EXTENDED_ADDR] = { NLA_U64 }, + [NL802154_DEVKEY_ATTR_ID] = { NLA_NESTED }, +}; + +static int nl802154_add_llsec_devkey(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg802154_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wpan_dev *wpan_dev = dev->ieee802154_ptr; + struct nlattr *attrs[NL802154_DEVKEY_ATTR_MAX + 1]; + struct ieee802154_llsec_device_key key; + __le64 extended_addr; + + if (!info->attrs[NL802154_ATTR_SEC_DEVKEY] || + nla_parse_nested(attrs, NL802154_DEVKEY_ATTR_MAX, + info->attrs[NL802154_ATTR_SEC_DEVKEY], + nl802154_devkey_policy) < 0) + return -EINVAL; + + if (!attrs[NL802154_DEVKEY_ATTR_FRAME_COUNTER] || + !attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR]) + return -EINVAL; + + /* TODO change key.id ? */ + if (ieee802154_llsec_parse_key_id(attrs[NL802154_DEVKEY_ATTR_ID], + &key.key_id) < 0) + return -ENOBUFS; + + /* TODO be32 */ + key.frame_counter = nla_get_u32(attrs[NL802154_DEVKEY_ATTR_FRAME_COUNTER]); + /* TODO change naming hwaddr -> extended_addr + * check unique identifier short+pan OR extended_addr + */ + extended_addr = nla_get_le64(attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR]); + return rdev_add_devkey(rdev, wpan_dev, extended_addr, &key); +} + +static int nl802154_del_llsec_devkey(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg802154_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wpan_dev *wpan_dev = dev->ieee802154_ptr; + struct nlattr *attrs[NL802154_DEVKEY_ATTR_MAX + 1]; + struct ieee802154_llsec_device_key key; + __le64 extended_addr; + + if (nla_parse_nested(attrs, NL802154_DEVKEY_ATTR_MAX, + info->attrs[NL802154_ATTR_SEC_DEVKEY], + nl802154_devkey_policy)) + return -EINVAL; + + if (!attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR]) + return -EINVAL; + + /* TODO change key.id ? */ + if (ieee802154_llsec_parse_key_id(attrs[NL802154_DEVKEY_ATTR_ID], + &key.key_id) < 0) + return -ENOBUFS; + + /* TODO change naming hwaddr -> extended_addr + * check unique identifier short+pan OR extended_addr + */ + extended_addr = nla_get_le64(attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR]); + return rdev_del_devkey(rdev, wpan_dev, extended_addr, &key); +} + +static int nl802154_send_seclevel(struct sk_buff *msg, u32 cmd, u32 portid, + u32 seq, int flags, + struct cfg802154_registered_device *rdev, + struct net_device *dev, + const struct ieee802154_llsec_seclevel *sl) +{ + void *hdr; + struct nlattr *nl_seclevel; + + hdr = nl802154hdr_put(msg, portid, seq, flags, cmd); + if (!hdr) + return -1; + + if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex)) + goto nla_put_failure; + + nl_seclevel = nla_nest_start(msg, NL802154_ATTR_SEC_LEVEL); + if (!nl_seclevel) + goto nla_put_failure; + + if (nla_put_u32(msg, NL802154_SECLEVEL_ATTR_FRAME, sl->frame_type) || + nla_put_u32(msg, NL802154_SECLEVEL_ATTR_LEVELS, sl->sec_levels) || + nla_put_u8(msg, NL802154_SECLEVEL_ATTR_DEV_OVERRIDE, + sl->device_override)) + goto nla_put_failure; + + if (sl->frame_type == NL802154_FRAME_CMD) { + if (nla_put_u32(msg, NL802154_SECLEVEL_ATTR_CMD_FRAME, + sl->cmd_frame_id)) + goto nla_put_failure; + } + + nla_nest_end(msg, nl_seclevel); + genlmsg_end(msg, hdr); + + return 0; + +nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +static int +nl802154_dump_llsec_seclevel(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct cfg802154_registered_device *rdev = NULL; + struct ieee802154_llsec_seclevel *sl; + struct ieee802154_llsec_table *table; + struct wpan_dev *wpan_dev; + int err; + + err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev); + if (err) + return err; + + if (!wpan_dev->netdev) { + err = -EINVAL; + goto out_err; + } + + rdev_lock_llsec_table(rdev, wpan_dev); + rdev_get_llsec_table(rdev, wpan_dev, &table); + + /* TODO make it like station dump */ + if (cb->args[2]) + goto out; + + list_for_each_entry(sl, &table->security_levels, list) { + if (nl802154_send_seclevel(skb, NL802154_CMD_NEW_SEC_LEVEL, + NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + rdev, wpan_dev->netdev, sl) < 0) { + /* TODO */ + err = -EIO; + rdev_unlock_llsec_table(rdev, wpan_dev); + goto out_err; + } + } + + cb->args[2] = 1; + +out: + rdev_unlock_llsec_table(rdev, wpan_dev); + err = skb->len; +out_err: + nl802154_finish_wpan_dev_dump(rdev); + + return err; +} + +static const struct nla_policy nl802154_seclevel_policy[NL802154_SECLEVEL_ATTR_MAX + 1] = { + [NL802154_SECLEVEL_ATTR_LEVELS] = { .type = NLA_U8 }, + [NL802154_SECLEVEL_ATTR_FRAME] = { .type = NLA_U32 }, + [NL802154_SECLEVEL_ATTR_CMD_FRAME] = { .type = NLA_U32 }, + [NL802154_SECLEVEL_ATTR_DEV_OVERRIDE] = { .type = NLA_U8 }, +}; + +static int +llsec_parse_seclevel(struct nlattr *nla, struct ieee802154_llsec_seclevel *sl) +{ + struct nlattr *attrs[NL802154_SECLEVEL_ATTR_MAX + 1]; + + if (!nla || nla_parse_nested(attrs, NL802154_SECLEVEL_ATTR_MAX, nla, + nl802154_seclevel_policy)) + return -EINVAL; + + memset(sl, 0, sizeof(*sl)); + + if (!attrs[NL802154_SECLEVEL_ATTR_LEVELS] || + !attrs[NL802154_SECLEVEL_ATTR_FRAME] || + !attrs[NL802154_SECLEVEL_ATTR_DEV_OVERRIDE]) + return -EINVAL; + + sl->sec_levels = nla_get_u8(attrs[NL802154_SECLEVEL_ATTR_LEVELS]); + sl->frame_type = nla_get_u32(attrs[NL802154_SECLEVEL_ATTR_FRAME]); + sl->device_override = nla_get_u8(attrs[NL802154_SECLEVEL_ATTR_DEV_OVERRIDE]); + if (sl->frame_type > NL802154_FRAME_MAX || + (sl->device_override != 0 && sl->device_override != 1)) + return -EINVAL; + + if (sl->frame_type == NL802154_FRAME_CMD) { + if (!attrs[NL802154_SECLEVEL_ATTR_CMD_FRAME]) + return -EINVAL; + + sl->cmd_frame_id = nla_get_u32(attrs[NL802154_SECLEVEL_ATTR_CMD_FRAME]); + if (sl->cmd_frame_id > NL802154_CMD_FRAME_MAX) + return -EINVAL; + } + + return 0; +} + +static int nl802154_add_llsec_seclevel(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg802154_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wpan_dev *wpan_dev = dev->ieee802154_ptr; + struct ieee802154_llsec_seclevel sl; + + if (llsec_parse_seclevel(info->attrs[NL802154_ATTR_SEC_LEVEL], + &sl) < 0) + return -EINVAL; + + return rdev_add_seclevel(rdev, wpan_dev, &sl); +} + +static int nl802154_del_llsec_seclevel(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg802154_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wpan_dev *wpan_dev = dev->ieee802154_ptr; + struct ieee802154_llsec_seclevel sl; + + if (!info->attrs[NL802154_ATTR_SEC_LEVEL] || + llsec_parse_seclevel(info->attrs[NL802154_ATTR_SEC_LEVEL], + &sl) < 0) + return -EINVAL; + + return rdev_del_seclevel(rdev, wpan_dev, &sl); +} +#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ + +#define NL802154_FLAG_NEED_WPAN_PHY 0x01 +#define NL802154_FLAG_NEED_NETDEV 0x02 +#define NL802154_FLAG_NEED_RTNL 0x04 +#define NL802154_FLAG_CHECK_NETDEV_UP 0x08 +#define NL802154_FLAG_NEED_NETDEV_UP (NL802154_FLAG_NEED_NETDEV |\ + NL802154_FLAG_CHECK_NETDEV_UP) +#define NL802154_FLAG_NEED_WPAN_DEV 0x10 +#define NL802154_FLAG_NEED_WPAN_DEV_UP (NL802154_FLAG_NEED_WPAN_DEV |\ + NL802154_FLAG_CHECK_NETDEV_UP) + +static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg802154_registered_device *rdev; + struct wpan_dev *wpan_dev; + struct net_device *dev; + bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL; + + if (rtnl) + rtnl_lock(); + + if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) { + rdev = cfg802154_get_dev_from_info(genl_info_net(info), info); + if (IS_ERR(rdev)) { + if (rtnl) + rtnl_unlock(); + return PTR_ERR(rdev); + } + info->user_ptr[0] = rdev; + } else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV || + ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) { + ASSERT_RTNL(); + wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info), + info->attrs); + if (IS_ERR(wpan_dev)) { + if (rtnl) + rtnl_unlock(); + return PTR_ERR(wpan_dev); + } + + dev = wpan_dev->netdev; + rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy); + + if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) { + if (!dev) { + if (rtnl) + rtnl_unlock(); + return -EINVAL; + } + + info->user_ptr[1] = dev; + } else { + info->user_ptr[1] = wpan_dev; + } + + if (dev) { + if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP && + !netif_running(dev)) { + if (rtnl) + rtnl_unlock(); + return -ENETDOWN; + } + + dev_hold(dev); + } + + info->user_ptr[0] = rdev; + } + + return 0; +} + +static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb, + struct genl_info *info) +{ + if (info->user_ptr[1]) { + if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) { + struct wpan_dev *wpan_dev = info->user_ptr[1]; + + if (wpan_dev->netdev) + dev_put(wpan_dev->netdev); + } else { + dev_put(info->user_ptr[1]); + } + } + + if (ops->internal_flags & NL802154_FLAG_NEED_RTNL) + rtnl_unlock(); +} + +static const struct genl_ops nl802154_ops[] = { + { + .cmd = NL802154_CMD_GET_WPAN_PHY, + .doit = nl802154_get_wpan_phy, + .dumpit = nl802154_dump_wpan_phy, + .done = nl802154_dump_wpan_phy_done, + .policy = nl802154_policy, + /* can be retrieved by unprivileged users */ + .internal_flags = NL802154_FLAG_NEED_WPAN_PHY | + NL802154_FLAG_NEED_RTNL, + }, + { + .cmd = NL802154_CMD_GET_INTERFACE, + .doit = nl802154_get_interface, + .dumpit = nl802154_dump_interface, + .policy = nl802154_policy, + /* can be retrieved by unprivileged users */ + .internal_flags = NL802154_FLAG_NEED_WPAN_DEV | + NL802154_FLAG_NEED_RTNL, + }, + { + .cmd = NL802154_CMD_NEW_INTERFACE, + .doit = nl802154_new_interface, + .policy = nl802154_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_WPAN_PHY | NL802154_FLAG_NEED_RTNL, }, { @@ -1287,6 +2302,119 @@ static const struct genl_ops nl802154_ops[] = { .internal_flags = NL802154_FLAG_NEED_NETDEV | NL802154_FLAG_NEED_RTNL, }, +#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL + { + .cmd = NL802154_CMD_SET_SEC_PARAMS, + .doit = nl802154_set_llsec_params, + .policy = nl802154_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_NETDEV | + NL802154_FLAG_NEED_RTNL, + }, + { + .cmd = NL802154_CMD_GET_SEC_KEY, + /* TODO .doit by matching key id? */ + .dumpit = nl802154_dump_llsec_key, + .policy = nl802154_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_NETDEV | + NL802154_FLAG_NEED_RTNL, + }, + { + .cmd = NL802154_CMD_NEW_SEC_KEY, + .doit = nl802154_add_llsec_key, + .policy = nl802154_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_NETDEV | + NL802154_FLAG_NEED_RTNL, + }, + { + .cmd = NL802154_CMD_DEL_SEC_KEY, + .doit = nl802154_del_llsec_key, + .policy = nl802154_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_NETDEV | + NL802154_FLAG_NEED_RTNL, + }, + /* TODO unique identifier must short+pan OR extended_addr */ + { + .cmd = NL802154_CMD_GET_SEC_DEV, + /* TODO .doit by matching extended_addr? */ + .dumpit = nl802154_dump_llsec_dev, + .policy = nl802154_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_NETDEV | + NL802154_FLAG_NEED_RTNL, + }, + { + .cmd = NL802154_CMD_NEW_SEC_DEV, + .doit = nl802154_add_llsec_dev, + .policy = nl802154_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_NETDEV | + NL802154_FLAG_NEED_RTNL, + }, + { + .cmd = NL802154_CMD_DEL_SEC_DEV, + .doit = nl802154_del_llsec_dev, + .policy = nl802154_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_NETDEV | + NL802154_FLAG_NEED_RTNL, + }, + /* TODO remove complete devkey, put it as nested? */ + { + .cmd = NL802154_CMD_GET_SEC_DEVKEY, + /* TODO doit by matching ??? */ + .dumpit = nl802154_dump_llsec_devkey, + .policy = nl802154_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_NETDEV | + NL802154_FLAG_NEED_RTNL, + }, + { + .cmd = NL802154_CMD_NEW_SEC_DEVKEY, + .doit = nl802154_add_llsec_devkey, + .policy = nl802154_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_NETDEV | + NL802154_FLAG_NEED_RTNL, + }, + { + .cmd = NL802154_CMD_DEL_SEC_DEVKEY, + .doit = nl802154_del_llsec_devkey, + .policy = nl802154_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_NETDEV | + NL802154_FLAG_NEED_RTNL, + }, + { + .cmd = NL802154_CMD_GET_SEC_LEVEL, + /* TODO .doit by matching frame_type? */ + .dumpit = nl802154_dump_llsec_seclevel, + .policy = nl802154_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_NETDEV | + NL802154_FLAG_NEED_RTNL, + }, + { + .cmd = NL802154_CMD_NEW_SEC_LEVEL, + .doit = nl802154_add_llsec_seclevel, + .policy = nl802154_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_NETDEV | + NL802154_FLAG_NEED_RTNL, + }, + { + .cmd = NL802154_CMD_DEL_SEC_LEVEL, + /* TODO match frame_type only? */ + .doit = nl802154_del_llsec_seclevel, + .policy = nl802154_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_NETDEV | + NL802154_FLAG_NEED_RTNL, + }, +#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ }; /* initialisation/exit functions */ diff --git a/net/ieee802154/rdev-ops.h b/net/ieee802154/rdev-ops.h index 03b3575..4441c63 100644 --- a/net/ieee802154/rdev-ops.h +++ b/net/ieee802154/rdev-ops.h @@ -208,4 +208,113 @@ rdev_set_ackreq_default(struct cfg802154_registered_device *rdev, return ret; } +#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL +/* TODO this is already a nl802154, so move into ieee802154 */ +static inline void +rdev_get_llsec_table(struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev, + struct ieee802154_llsec_table **table) +{ + rdev->ops->get_llsec_table(&rdev->wpan_phy, wpan_dev, table); +} + +static inline void +rdev_lock_llsec_table(struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev) +{ + rdev->ops->lock_llsec_table(&rdev->wpan_phy, wpan_dev); +} + +static inline void +rdev_unlock_llsec_table(struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev) +{ + rdev->ops->unlock_llsec_table(&rdev->wpan_phy, wpan_dev); +} + +static inline int +rdev_get_llsec_params(struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev, + struct ieee802154_llsec_params *params) +{ + return rdev->ops->get_llsec_params(&rdev->wpan_phy, wpan_dev, params); +} + +static inline int +rdev_set_llsec_params(struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev, + const struct ieee802154_llsec_params *params, + u32 changed) +{ + return rdev->ops->set_llsec_params(&rdev->wpan_phy, wpan_dev, params, + changed); +} + +static inline int +rdev_add_llsec_key(struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev, + const struct ieee802154_llsec_key_id *id, + const struct ieee802154_llsec_key *key) +{ + return rdev->ops->add_llsec_key(&rdev->wpan_phy, wpan_dev, id, key); +} + +static inline int +rdev_del_llsec_key(struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev, + const struct ieee802154_llsec_key_id *id) +{ + return rdev->ops->del_llsec_key(&rdev->wpan_phy, wpan_dev, id); +} + +static inline int +rdev_add_seclevel(struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev, + const struct ieee802154_llsec_seclevel *sl) +{ + return rdev->ops->add_seclevel(&rdev->wpan_phy, wpan_dev, sl); +} + +static inline int +rdev_del_seclevel(struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev, + const struct ieee802154_llsec_seclevel *sl) +{ + return rdev->ops->del_seclevel(&rdev->wpan_phy, wpan_dev, sl); +} + +static inline int +rdev_add_device(struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev, + const struct ieee802154_llsec_device *dev_desc) +{ + return rdev->ops->add_device(&rdev->wpan_phy, wpan_dev, dev_desc); +} + +static inline int +rdev_del_device(struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev, __le64 extended_addr) +{ + return rdev->ops->del_device(&rdev->wpan_phy, wpan_dev, extended_addr); +} + +static inline int +rdev_add_devkey(struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev, __le64 extended_addr, + const struct ieee802154_llsec_device_key *devkey) +{ + return rdev->ops->add_devkey(&rdev->wpan_phy, wpan_dev, extended_addr, + devkey); +} + +static inline int +rdev_del_devkey(struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev, __le64 extended_addr, + const struct ieee802154_llsec_device_key *devkey) +{ + return rdev->ops->del_devkey(&rdev->wpan_phy, wpan_dev, extended_addr, + devkey); +} +#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ + #endif /* __CFG802154_RDEV_OPS */ diff --git a/net/mac802154/cfg.c b/net/mac802154/cfg.c index c865ebb..57b5e94 100644 --- a/net/mac802154/cfg.c +++ b/net/mac802154/cfg.c @@ -266,6 +266,195 @@ ieee802154_set_ackreq_default(struct wpan_phy *wpan_phy, return 0; } +#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL +static void +ieee802154_get_llsec_table(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev, + struct ieee802154_llsec_table **table) +{ + struct net_device *dev = wpan_dev->netdev; + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + + *table = &sdata->sec.table; +} + +static void +ieee802154_lock_llsec_table(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev) +{ + struct net_device *dev = wpan_dev->netdev; + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + + mutex_lock(&sdata->sec_mtx); +} + +static void +ieee802154_unlock_llsec_table(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev) +{ + struct net_device *dev = wpan_dev->netdev; + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + + mutex_unlock(&sdata->sec_mtx); +} + +static int +ieee802154_set_llsec_params(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev, + const struct ieee802154_llsec_params *params, + int changed) +{ + struct net_device *dev = wpan_dev->netdev; + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + int res; + + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_set_params(&sdata->sec, params, changed); + mutex_unlock(&sdata->sec_mtx); + + return res; +} + +static int +ieee802154_get_llsec_params(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev, + struct ieee802154_llsec_params *params) +{ + struct net_device *dev = wpan_dev->netdev; + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + int res; + + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_get_params(&sdata->sec, params); + mutex_unlock(&sdata->sec_mtx); + + return res; +} + +static int +ieee802154_add_llsec_key(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, + const struct ieee802154_llsec_key_id *id, + const struct ieee802154_llsec_key *key) +{ + struct net_device *dev = wpan_dev->netdev; + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + int res; + + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_key_add(&sdata->sec, id, key); + mutex_unlock(&sdata->sec_mtx); + + return res; +} + +static int +ieee802154_del_llsec_key(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, + const struct ieee802154_llsec_key_id *id) +{ + struct net_device *dev = wpan_dev->netdev; + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + int res; + + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_key_del(&sdata->sec, id); + mutex_unlock(&sdata->sec_mtx); + + return res; +} + +static int +ieee802154_add_seclevel(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, + const struct ieee802154_llsec_seclevel *sl) +{ + struct net_device *dev = wpan_dev->netdev; + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + int res; + + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_seclevel_add(&sdata->sec, sl); + mutex_unlock(&sdata->sec_mtx); + + return res; +} + +static int +ieee802154_del_seclevel(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, + const struct ieee802154_llsec_seclevel *sl) +{ + struct net_device *dev = wpan_dev->netdev; + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + int res; + + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_seclevel_del(&sdata->sec, sl); + mutex_unlock(&sdata->sec_mtx); + + return res; +} + +static int +ieee802154_add_device(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, + const struct ieee802154_llsec_device *dev_desc) +{ + struct net_device *dev = wpan_dev->netdev; + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + int res; + + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_dev_add(&sdata->sec, dev_desc); + mutex_unlock(&sdata->sec_mtx); + + return res; +} + +static int +ieee802154_del_device(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, + __le64 extended_addr) +{ + struct net_device *dev = wpan_dev->netdev; + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + int res; + + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_dev_del(&sdata->sec, extended_addr); + mutex_unlock(&sdata->sec_mtx); + + return res; +} + +static int +ieee802154_add_devkey(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, + __le64 extended_addr, + const struct ieee802154_llsec_device_key *key) +{ + struct net_device *dev = wpan_dev->netdev; + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + int res; + + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_devkey_add(&sdata->sec, extended_addr, key); + mutex_unlock(&sdata->sec_mtx); + + return res; +} + +static int +ieee802154_del_devkey(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, + __le64 extended_addr, + const struct ieee802154_llsec_device_key *key) +{ + struct net_device *dev = wpan_dev->netdev; + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + int res; + + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_devkey_del(&sdata->sec, extended_addr, key); + mutex_unlock(&sdata->sec_mtx); + + return res; +} +#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ + const struct cfg802154_ops mac802154_config_ops = { .add_virtual_intf_deprecated = ieee802154_add_iface_deprecated, .del_virtual_intf_deprecated = ieee802154_del_iface_deprecated, @@ -284,4 +473,20 @@ const struct cfg802154_ops mac802154_config_ops = { .set_max_frame_retries = ieee802154_set_max_frame_retries, .set_lbt_mode = ieee802154_set_lbt_mode, .set_ackreq_default = ieee802154_set_ackreq_default, +#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL + .get_llsec_table = ieee802154_get_llsec_table, + .lock_llsec_table = ieee802154_lock_llsec_table, + .unlock_llsec_table = ieee802154_unlock_llsec_table, + /* TODO above */ + .set_llsec_params = ieee802154_set_llsec_params, + .get_llsec_params = ieee802154_get_llsec_params, + .add_llsec_key = ieee802154_add_llsec_key, + .del_llsec_key = ieee802154_del_llsec_key, + .add_seclevel = ieee802154_add_seclevel, + .del_seclevel = ieee802154_del_seclevel, + .add_device = ieee802154_add_device, + .del_device = ieee802154_del_device, + .add_devkey = ieee802154_add_devkey, + .del_devkey = ieee802154_del_devkey, +#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ }; -- cgit v0.10.2 From d58a2fa903c18f97aac30cd3c4c8a378a2c647c4 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 28 Sep 2015 09:00:26 +0200 Subject: mac802154: add comments for llsec issues While doing a little test with the llsec implementation I saw these issues. We should move decryption and encruption somewhere else, otherwise while capturing with wireshark the mac header shows secuirty fields but the payload is plaintext. A complete other issue is what doing with HardMAC drivers where the payload is always plaintext. I think we need a special handling then in userspace. We currently doesn't support any HardMAC transceivers, so we should fix the first issue for SoftMAC transceivers. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c index d1c33c1..42e9672 100644 --- a/net/mac802154/rx.c +++ b/net/mac802154/rx.c @@ -87,6 +87,10 @@ ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata, skb->dev = sdata->dev; + /* TODO this should be moved after netif_receive_skb call, otherwise + * wireshark will show a mac header with security fields and the + * payload is already decrypted. + */ rc = mac802154_llsec_decrypt(&sdata->sec, skb); if (rc) { pr_debug("decryption failed: %i\n", rc); diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c index 5ee596e..b205bbe 100644 --- a/net/mac802154/tx.c +++ b/net/mac802154/tx.c @@ -129,6 +129,10 @@ ieee802154_subif_start_xmit(struct sk_buff *skb, struct net_device *dev) struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); int rc; + /* TODO we should move it to wpan_dev_hard_header and dev_hard_header + * functions. The reason is wireshark will show a mac header which is + * with security fields but the payload is not encrypted. + */ rc = mac802154_llsec_encrypt(&sdata->sec, skb); if (rc) { netdev_warn(dev, "encryption failed: %i\n", rc); -- cgit v0.10.2 From b40988c438c2405a177ae54ff4baa08c720c296f Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 28 Sep 2015 12:36:26 +0200 Subject: ieee802154: change mtu size behaviour This patch changes the mtu size of 802.15.4 interfaces. The current setting is the meaning of the maximum transport unit with mac header, which is 127 bytes according 802.15.4. The linux meaning of the mtu size field is the maximum payload of a mac frame. Like in ethernet, which is 1500 bytes. We have dynamic length of mac frames in 802.15.4, this is why we assume the minimum header length which is hard_header_len. This contains fc and sequence fields. These can evaluated by driver layer without additional checks. We currently don't support to set the FCS from userspace, so we need to subtract this from mtu size as well. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/ieee802154/socket.c b/net/ieee802154/socket.c index be77f21..a548be2 100644 --- a/net/ieee802154/socket.c +++ b/net/ieee802154/socket.c @@ -273,7 +273,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) goto out; } - mtu = dev->mtu; + mtu = IEEE802154_MTU; pr_debug("name = %s, mtu = %u\n", dev->name, mtu); if (size > mtu) { @@ -637,7 +637,7 @@ static int dgram_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) err = -ENXIO; goto out; } - mtu = dev->mtu; + mtu = IEEE802154_MTU; pr_debug("name = %s, mtu = %u\n", dev->name, mtu); if (size > mtu) { diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c index 3954bcf..7079cd3 100644 --- a/net/mac802154/iface.c +++ b/net/mac802154/iface.c @@ -547,7 +547,17 @@ static void ieee802154_if_setup(struct net_device *dev) */ dev->needed_tailroom = IEEE802154_MAX_AUTH_TAG_LEN + IEEE802154_FCS_LEN; - dev->mtu = IEEE802154_MTU; + /* The mtu size is the payload without mac header in this case. + * We have a dynamic length header with a minimum header length + * which is hard_header_len. In this case we let mtu to the size + * of maximum payload which is IEEE802154_MTU - IEEE802154_FCS_LEN - + * hard_header_len. The FCS which is set by hardware or ndo_start_xmit + * and the minimum mac header which can be evaluated inside driver + * layer. The rest of mac header will be part of payload if greater + * than hard_header_len. + */ + dev->mtu = IEEE802154_MTU - IEEE802154_FCS_LEN - + dev->hard_header_len; dev->tx_queue_len = 300; dev->flags = IFF_NOARP | IFF_BROADCAST; } diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c index b205bbe..3827f35 100644 --- a/net/mac802154/tx.c +++ b/net/mac802154/tx.c @@ -71,17 +71,6 @@ ieee802154_tx(struct ieee802154_local *local, struct sk_buff *skb) struct net_device *dev = skb->dev; int ret; - /* This check is for AF_PACKET RAW socket only, which doesn't - * know about the FCS which is set here or by hardware. otherwise - * it should not occur in any case! - * - * TODO: This should be handled in AF_PACKET and return -EMSGSIZE. - */ - if (skb->len > IEEE802154_MTU - IEEE802154_FCS_LEN) { - netdev_warn(dev, "Frame len above MTU limit. Dropped.\n"); - goto err_tx; - } - if (!(local->hw.flags & IEEE802154_HW_TX_OMIT_CKSUM)) { u16 crc = crc_ccitt(0, skb->data, skb->len); -- cgit v0.10.2 From 5f2ebb3b59c81a461ffd4921d12e9f6e09c32945 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 28 Sep 2015 19:59:48 +0200 Subject: mac802154: check on len instead mac_len This patch change the length check to len instead of mac_len for checking if the frame control field is available to dereference. We need to change it because I saw issues with af_packet raw sockets and the mrf24j40 which calls this functionality. The raw socket functionality doesn't set the mac_len but resets the skb_mac_header to skb->data which is still correct. The issue occur at mrf24j40 only, because the driver need to evaluate the fc fields. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/include/net/mac802154.h b/include/net/mac802154.h index 2c47850..5718765 100644 --- a/include/net/mac802154.h +++ b/include/net/mac802154.h @@ -248,7 +248,7 @@ struct ieee802154_ops { static inline __le16 ieee802154_get_fc_from_skb(const struct sk_buff *skb) { /* return some invalid fc on failure */ - if (unlikely(skb->mac_len < 2)) { + if (unlikely(skb->len < 2)) { WARN_ON(1); return cpu_to_le16(0); } -- cgit v0.10.2 From 72d53b116264d5e570f610b3971dae4721aa5c0f Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Wed, 30 Sep 2015 10:20:09 +0200 Subject: ieee802154: 6lowpan: change datagram var types This patch changes datagram size variable from u16 type to unsigned int. The reason is that an IPv6 header has an MAX_UIN16 payload length, but the datagram size is payload + IPv6 header length. This avoids overflows at some places. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/ieee802154/6lowpan/6lowpan_i.h b/net/ieee802154/6lowpan/6lowpan_i.h index b4e17a7..10d44d0 100644 --- a/net/ieee802154/6lowpan/6lowpan_i.h +++ b/net/ieee802154/6lowpan/6lowpan_i.h @@ -18,7 +18,7 @@ typedef unsigned __bitwise__ lowpan_rx_result; struct lowpan_create_arg { u16 tag; - u16 d_size; + unsigned int d_size; const struct ieee802154_addr *src; const struct ieee802154_addr *dst; }; @@ -29,7 +29,7 @@ struct lowpan_frag_queue { struct inet_frag_queue q; u16 tag; - u16 d_size; + unsigned int d_size; struct ieee802154_addr saddr; struct ieee802154_addr daddr; }; diff --git a/net/ieee802154/6lowpan/reassembly.c b/net/ieee802154/6lowpan/reassembly.c index 12e8cf4..af663cb 100644 --- a/net/ieee802154/6lowpan/reassembly.c +++ b/net/ieee802154/6lowpan/reassembly.c @@ -37,7 +37,7 @@ static struct inet_frags lowpan_frags; static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *prev, struct net_device *ldev); -static unsigned int lowpan_hash_frag(u16 tag, u16 d_size, +static unsigned int lowpan_hash_frag(u16 tag, unsigned int d_size, const struct ieee802154_addr *saddr, const struct ieee802154_addr *daddr) { diff --git a/net/ieee802154/6lowpan/tx.c b/net/ieee802154/6lowpan/tx.c index 7e0563e..5ecf8af 100644 --- a/net/ieee802154/6lowpan/tx.c +++ b/net/ieee802154/6lowpan/tx.c @@ -131,8 +131,8 @@ lowpan_xmit_fragment(struct sk_buff *skb, const struct ieee802154_hdr *wpan_hdr, static int lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *ldev, - const struct ieee802154_hdr *wpan_hdr, u16 dgram_size, - u16 dgram_offset) + const struct ieee802154_hdr *wpan_hdr, + unsigned int dgram_size, unsigned int dgram_offset) { __be16 frag_tag; u8 frag_hdr[5]; @@ -194,7 +194,7 @@ err: } static int lowpan_header(struct sk_buff *skb, struct net_device *ldev, - u16 *dgram_size, u16 *dgram_offset) + unsigned int *dgram_size, unsigned int *dgram_offset) { struct wpan_dev *wpan_dev = lowpan_dev_info(ldev)->wdev->ieee802154_ptr; struct ieee802154_addr sa, da; @@ -244,7 +244,7 @@ netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *ldev) { struct ieee802154_hdr wpan_hdr; int max_single, ret; - u16 dgram_size, dgram_offset; + unsigned int dgram_size, dgram_offset; pr_debug("package xmit\n"); -- cgit v0.10.2 From 4bc8fbc95e0d831e5e3800ecc8a8d5acac79c9a8 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Wed, 30 Sep 2015 10:20:10 +0200 Subject: ieee802154: 6lowpan: don't skip first dsn while fragmentation This patch fixes the data frame sequence numer (dsn) while 6lowpan fragmentation for frag1. Currently we create one 802.15.4 header at first, then check if it's match into one frame and at the end construct many fragments and calling wpan_dev_hard_header for each of them, inclusive for the first fragment. This will make the first generated header to garbage, instead we copying this header for frag1 instead of generate a new one which skips one dsn. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/ieee802154/6lowpan/tx.c b/net/ieee802154/6lowpan/tx.c index 5ecf8af..3b665e1 100644 --- a/net/ieee802154/6lowpan/tx.c +++ b/net/ieee802154/6lowpan/tx.c @@ -79,7 +79,7 @@ int lowpan_header_create(struct sk_buff *skb, struct net_device *ldev, static struct sk_buff* lowpan_alloc_frag(struct sk_buff *skb, int size, - const struct ieee802154_hdr *master_hdr) + const struct ieee802154_hdr *master_hdr, bool frag1) { struct net_device *wdev = lowpan_dev_info(skb->dev)->wdev; struct sk_buff *frag; @@ -95,11 +95,17 @@ lowpan_alloc_frag(struct sk_buff *skb, int size, skb_reset_network_header(frag); *mac_cb(frag) = *mac_cb(skb); - rc = wpan_dev_hard_header(frag, wdev, &master_hdr->dest, - &master_hdr->source, size); - if (rc < 0) { - kfree_skb(frag); - return ERR_PTR(rc); + if (frag1) { + memcpy(skb_put(frag, skb->mac_len), + skb_mac_header(skb), skb->mac_len); + } else { + rc = wpan_dev_hard_header(frag, wdev, + &master_hdr->dest, + &master_hdr->source, size); + if (rc < 0) { + kfree_skb(frag); + return ERR_PTR(rc); + } } } else { frag = ERR_PTR(-ENOMEM); @@ -111,13 +117,13 @@ lowpan_alloc_frag(struct sk_buff *skb, int size, static int lowpan_xmit_fragment(struct sk_buff *skb, const struct ieee802154_hdr *wpan_hdr, u8 *frag_hdr, int frag_hdrlen, - int offset, int len) + int offset, int len, bool frag1) { struct sk_buff *frag; raw_dump_inline(__func__, " fragment header", frag_hdr, frag_hdrlen); - frag = lowpan_alloc_frag(skb, frag_hdrlen + len, wpan_hdr); + frag = lowpan_alloc_frag(skb, frag_hdrlen + len, wpan_hdr, frag1); if (IS_ERR(frag)) return PTR_ERR(frag); @@ -156,7 +162,8 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *ldev, rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr, LOWPAN_FRAG1_HEAD_SIZE, 0, - frag_len + skb_network_header_len(skb)); + frag_len + skb_network_header_len(skb), + true); if (rc) { pr_debug("%s unable to send FRAG1 packet (tag: %d)", __func__, ntohs(frag_tag)); @@ -177,7 +184,7 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *ldev, rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr, LOWPAN_FRAGN_HEAD_SIZE, skb_offset, - frag_len); + frag_len, false); if (rc) { pr_debug("%s unable to send a FRAGN packet. (tag: %d, offset: %d)\n", __func__, ntohs(frag_tag), skb_offset); -- cgit v0.10.2 From 1c64f147d3cc9bbafe091a7b335ea3ec700186f0 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Wed, 30 Sep 2015 10:20:11 +0200 Subject: ieee802154: 6lowpan: add tx/rx stats This patch adds support for increment transmit and receive stats. The meaning of these stats are IPv6 based, which shows the stats after running the 6lowpan adaptation layer (uncompression/compression, fragmentation handling) on receive and before the adaptation layer when transmit. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/ieee802154/6lowpan/rx.c b/net/ieee802154/6lowpan/rx.c index b1fd47d..65d55e0 100644 --- a/net/ieee802154/6lowpan/rx.c +++ b/net/ieee802154/6lowpan/rx.c @@ -29,6 +29,8 @@ static int lowpan_give_skb_to_device(struct sk_buff *skb) { skb->protocol = htons(ETH_P_IPV6); + skb->dev->stats.rx_packets++; + skb->dev->stats.rx_bytes += skb->len; return netif_rx(skb); } diff --git a/net/ieee802154/6lowpan/tx.c b/net/ieee802154/6lowpan/tx.c index 3b665e1..5736302 100644 --- a/net/ieee802154/6lowpan/tx.c +++ b/net/ieee802154/6lowpan/tx.c @@ -192,6 +192,8 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *ldev, } } while (skb_unprocessed > frag_cap); + ldev->stats.tx_packets++; + ldev->stats.tx_bytes += dgram_size; consume_skb(skb); return NET_XMIT_SUCCESS; @@ -277,6 +279,8 @@ netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *ldev) if (skb_tail_pointer(skb) - skb_network_header(skb) <= max_single) { skb->dev = lowpan_dev_info(ldev)->wdev; + ldev->stats.tx_packets++; + ldev->stats.tx_bytes += dgram_size; return dev_queue_xmit(skb); } else { netdev_tx_t rc; -- cgit v0.10.2 From 239ede3c0213f152a1bab083af7d7bbb39e0d3d5 Mon Sep 17 00:00:00 2001 From: Prasanna Karthik Date: Wed, 30 Sep 2015 12:27:46 +0000 Subject: Bluetooth: btuart_cs: remove obsolete header Use instead of , fixes checkpatch Warning; Signed-off-by: Prasanna Karthik Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index dfaa1c9..4c001a8 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include #include -- cgit v0.10.2 From 4a2fa2b8a861b095f6d495dc006e9d47df177594 Mon Sep 17 00:00:00 2001 From: Prasanna Karthik Date: Wed, 30 Sep 2015 13:02:05 +0000 Subject: Bluetooth: hci_h5: clean up hci_h5 code This patch fixes checkpatch warnings: - Comparison to NULL could be re-written - no space required after a cast Signed-off-by: Prasanna Karthik Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index b35b238..abee221 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -128,7 +128,7 @@ static void h5_timed_event(unsigned long arg) { const unsigned char sync_req[] = { 0x01, 0x7e }; unsigned char conf_req[] = { 0x03, 0xfc, 0x01 }; - struct hci_uart *hu = (struct hci_uart *) arg; + struct hci_uart *hu = (struct hci_uart *)arg; struct h5 *h5 = hu->priv; struct sk_buff *skb; unsigned long flags; @@ -210,7 +210,7 @@ static int h5_open(struct hci_uart *hu) init_timer(&h5->timer); h5->timer.function = h5_timed_event; - h5->timer.data = (unsigned long) hu; + h5->timer.data = (unsigned long)hu; h5->tx_win = H5_TX_WIN_MAX; @@ -453,7 +453,7 @@ static int h5_rx_pkt_start(struct hci_uart *hu, unsigned char c) return -ENOMEM; } - h5->rx_skb->dev = (void *) hu->hdev; + h5->rx_skb->dev = (void *)hu->hdev; return 0; } @@ -696,7 +696,7 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu) } skb = skb_dequeue(&h5->unrel); - if (skb != NULL) { + if (skb) { nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type, skb->data, skb->len); if (nskb) { @@ -714,7 +714,7 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu) goto unlock; skb = skb_dequeue(&h5->rel); - if (skb != NULL) { + if (skb) { nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type, skb->data, skb->len); if (nskb) { -- cgit v0.10.2 From 59fe4606748f7016e9e02bbd26da185f8620661a Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Fri, 3 Feb 2012 11:09:23 -0500 Subject: RDS: use kfree_rcu in rds_ib_remove_ipaddr synchronize_rcu() slowing down un-necessarily the socket shutdown path. It is used just kfree() the ip addresses in rds_ib_remove_ipaddr() which is perfect usecase for kfree_rcu(); So lets use that to gain some speedup. Signed-off-by: Santosh Shilimkar Signed-off-by: Santosh Shilimkar diff --git a/net/rds/ib.h b/net/rds/ib.h index aae60fd..f1fd5ff 100644 --- a/net/rds/ib.h +++ b/net/rds/ib.h @@ -164,6 +164,7 @@ struct rds_ib_connection { struct rds_ib_ipaddr { struct list_head list; __be32 ipaddr; + struct rcu_head rcu; }; struct rds_ib_device { diff --git a/net/rds/ib_rdma.c b/net/rds/ib_rdma.c index 251d1ce..872f523 100644 --- a/net/rds/ib_rdma.c +++ b/net/rds/ib_rdma.c @@ -159,10 +159,8 @@ static void rds_ib_remove_ipaddr(struct rds_ib_device *rds_ibdev, __be32 ipaddr) } spin_unlock_irq(&rds_ibdev->spinlock); - if (to_free) { - synchronize_rcu(); - kfree(to_free); - } + if (to_free) + kfree_rcu(to_free, rcu); } int rds_ib_update_ipaddr(struct rds_ib_device *rds_ibdev, __be32 ipaddr) -- cgit v0.10.2 From 8b0a6b461e2ccc95363e0547aa4f43ba2e02b096 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Fri, 3 Feb 2012 11:09:23 -0500 Subject: RDS: make socket bind/release locking scheme simple and more efficient RDS bind and release locking scheme is very inefficient. It uses RCU for maintaining the bind hash-table which is great but it also needs to hold spinlock for [add/remove]_bound(). So overall usecase, the hash-table concurrent speedup doesn't pay off. In fact blocking nature of synchronize_rcu() makes the RDS socket shutdown too slow which hurts RDS performance since connection shutdown and re-connect happens quite often to maintain the RC part of the protocol. So we make the locking scheme simpler and more efficient by replacing spin_locks with reader/writer locks and getting rid off rcu for bind hash-table. In subsequent patch, we also covert the global lock with per-bucket lock to reduce the global lock contention. Signed-off-by: Santosh Shilimkar Signed-off-by: Santosh Shilimkar diff --git a/net/rds/af_rds.c b/net/rds/af_rds.c index a2f28a6..dc08766 100644 --- a/net/rds/af_rds.c +++ b/net/rds/af_rds.c @@ -72,13 +72,7 @@ static int rds_release(struct socket *sock) rds_clear_recv_queue(rs); rds_cong_remove_socket(rs); - /* - * the binding lookup hash uses rcu, we need to - * make sure we synchronize_rcu before we free our - * entry - */ rds_remove_bound(rs); - synchronize_rcu(); rds_send_drop_to(rs, NULL); rds_rdma_drop_keys(rs); diff --git a/net/rds/bind.c b/net/rds/bind.c index dd666fb..01989e2 100644 --- a/net/rds/bind.c +++ b/net/rds/bind.c @@ -40,7 +40,7 @@ #define BIND_HASH_SIZE 1024 static struct hlist_head bind_hash_table[BIND_HASH_SIZE]; -static DEFINE_SPINLOCK(rds_bind_lock); +static DEFINE_RWLOCK(rds_bind_lock); static struct hlist_head *hash_to_bucket(__be32 addr, __be16 port) { @@ -48,6 +48,7 @@ static struct hlist_head *hash_to_bucket(__be32 addr, __be16 port) (BIND_HASH_SIZE - 1)); } +/* must hold either read or write lock (write lock for insert != NULL) */ static struct rds_sock *rds_bind_lookup(__be32 addr, __be16 port, struct rds_sock *insert) { @@ -56,30 +57,24 @@ static struct rds_sock *rds_bind_lookup(__be32 addr, __be16 port, u64 cmp; u64 needle = ((u64)be32_to_cpu(addr) << 32) | be16_to_cpu(port); - rcu_read_lock(); - hlist_for_each_entry_rcu(rs, head, rs_bound_node) { + hlist_for_each_entry(rs, head, rs_bound_node) { cmp = ((u64)be32_to_cpu(rs->rs_bound_addr) << 32) | be16_to_cpu(rs->rs_bound_port); - if (cmp == needle) { - rcu_read_unlock(); + if (cmp == needle) return rs; - } } - rcu_read_unlock(); if (insert) { /* * make sure our addr and port are set before - * we are added to the list, other people - * in rcu will find us as soon as the - * hlist_add_head_rcu is done + * we are added to the list. */ insert->rs_bound_addr = addr; insert->rs_bound_port = port; rds_sock_addref(insert); - hlist_add_head_rcu(&insert->rs_bound_node, head); + hlist_add_head(&insert->rs_bound_node, head); } return NULL; } @@ -93,8 +88,11 @@ static struct rds_sock *rds_bind_lookup(__be32 addr, __be16 port, struct rds_sock *rds_find_bound(__be32 addr, __be16 port) { struct rds_sock *rs; + unsigned long flags; + read_lock_irqsave(&rds_bind_lock, flags); rs = rds_bind_lookup(addr, port, NULL); + read_unlock_irqrestore(&rds_bind_lock, flags); if (rs && !sock_flag(rds_rs_to_sk(rs), SOCK_DEAD)) rds_sock_addref(rs); @@ -103,6 +101,7 @@ struct rds_sock *rds_find_bound(__be32 addr, __be16 port) rdsdebug("returning rs %p for %pI4:%u\n", rs, &addr, ntohs(port)); + return rs; } @@ -121,7 +120,7 @@ static int rds_add_bound(struct rds_sock *rs, __be32 addr, __be16 *port) last = rover - 1; } - spin_lock_irqsave(&rds_bind_lock, flags); + write_lock_irqsave(&rds_bind_lock, flags); do { if (rover == 0) @@ -135,7 +134,7 @@ static int rds_add_bound(struct rds_sock *rs, __be32 addr, __be16 *port) } } while (rover++ != last); - spin_unlock_irqrestore(&rds_bind_lock, flags); + write_unlock_irqrestore(&rds_bind_lock, flags); return ret; } @@ -144,19 +143,19 @@ void rds_remove_bound(struct rds_sock *rs) { unsigned long flags; - spin_lock_irqsave(&rds_bind_lock, flags); + write_lock_irqsave(&rds_bind_lock, flags); if (rs->rs_bound_addr) { rdsdebug("rs %p unbinding from %pI4:%d\n", rs, &rs->rs_bound_addr, ntohs(rs->rs_bound_port)); - hlist_del_init_rcu(&rs->rs_bound_node); + hlist_del_init(&rs->rs_bound_node); rds_sock_put(rs); rs->rs_bound_addr = 0; } - spin_unlock_irqrestore(&rds_bind_lock, flags); + write_unlock_irqrestore(&rds_bind_lock, flags); } int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) @@ -200,9 +199,5 @@ int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) out: release_sock(sk); - - /* we might have called rds_remove_bound on error */ - if (ret) - synchronize_rcu(); return ret; } -- cgit v0.10.2 From 28126959882d3ec4745f2ec800f3a1d74368b2fe Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Fri, 3 Feb 2012 11:08:50 -0500 Subject: RDS: fix rds_sock reference bug while doing bind One need to take rds socket reference while using it and release it once done with it. rds_add_bind() code path does not do that so lets fix it. Signed-off-by: Santosh Shilimkar Signed-off-by: Santosh Shilimkar diff --git a/net/rds/bind.c b/net/rds/bind.c index 01989e2..166c605 100644 --- a/net/rds/bind.c +++ b/net/rds/bind.c @@ -61,8 +61,10 @@ static struct rds_sock *rds_bind_lookup(__be32 addr, __be16 port, cmp = ((u64)be32_to_cpu(rs->rs_bound_addr) << 32) | be16_to_cpu(rs->rs_bound_port); - if (cmp == needle) + if (cmp == needle) { + rds_sock_addref(rs); return rs; + } } if (insert) { @@ -94,10 +96,10 @@ struct rds_sock *rds_find_bound(__be32 addr, __be16 port) rs = rds_bind_lookup(addr, port, NULL); read_unlock_irqrestore(&rds_bind_lock, flags); - if (rs && !sock_flag(rds_rs_to_sk(rs), SOCK_DEAD)) - rds_sock_addref(rs); - else + if (rs && sock_flag(rds_rs_to_sk(rs), SOCK_DEAD)) { + rds_sock_put(rs); rs = NULL; + } rdsdebug("returning rs %p for %pI4:%u\n", rs, &addr, ntohs(port)); @@ -123,14 +125,18 @@ static int rds_add_bound(struct rds_sock *rs, __be32 addr, __be16 *port) write_lock_irqsave(&rds_bind_lock, flags); do { + struct rds_sock *rrs; if (rover == 0) rover++; - if (!rds_bind_lookup(addr, cpu_to_be16(rover), rs)) { + rrs = rds_bind_lookup(addr, cpu_to_be16(rover), rs); + if (!rrs) { *port = rs->rs_bound_port; ret = 0; rdsdebug("rs %p binding to %pI4:%d\n", rs, &addr, (int)ntohs(*port)); break; + } else { + rds_sock_put(rrs); } } while (rover++ != last); -- cgit v0.10.2 From 9b9acde7e887e057568cd077d9c3377d2cb9aa5b Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Tue, 11 Feb 2014 19:34:25 -0800 Subject: RDS: Use per-bucket rw lock for bind hash-table One global lock protecting hash-tables with 1024 buckets isn't efficient and it shows up in a massive systems with truck loads of RDS sockets serving multiple databases. The perf data clearly highlights the contention on the rw lock in these massive workloads. When the contention gets worse, the code gets into a state where it decides to back off on the lock. So while it has disabled interrupts, it sits and backs off on this lock get. This causes the system to become sluggish and eventually all sorts of bad things happen. The simple fix is to move the lock into the hash bucket and use per-bucket lock to improve the scalability. Signed-off-by: Santosh Shilimkar Signed-off-by: Santosh Shilimkar diff --git a/net/rds/af_rds.c b/net/rds/af_rds.c index dc08766..384ea1e 100644 --- a/net/rds/af_rds.c +++ b/net/rds/af_rds.c @@ -582,6 +582,8 @@ static int rds_init(void) { int ret; + rds_bind_lock_init(); + ret = rds_conn_init(); if (ret) goto out; diff --git a/net/rds/bind.c b/net/rds/bind.c index 166c605..bc6b93e 100644 --- a/net/rds/bind.c +++ b/net/rds/bind.c @@ -38,22 +38,27 @@ #include #include "rds.h" +struct bind_bucket { + rwlock_t lock; + struct hlist_head head; +}; + #define BIND_HASH_SIZE 1024 -static struct hlist_head bind_hash_table[BIND_HASH_SIZE]; -static DEFINE_RWLOCK(rds_bind_lock); +static struct bind_bucket bind_hash_table[BIND_HASH_SIZE]; -static struct hlist_head *hash_to_bucket(__be32 addr, __be16 port) +static struct bind_bucket *hash_to_bucket(__be32 addr, __be16 port) { return bind_hash_table + (jhash_2words((u32)addr, (u32)port, 0) & (BIND_HASH_SIZE - 1)); } /* must hold either read or write lock (write lock for insert != NULL) */ -static struct rds_sock *rds_bind_lookup(__be32 addr, __be16 port, +static struct rds_sock *rds_bind_lookup(struct bind_bucket *bucket, + __be32 addr, __be16 port, struct rds_sock *insert) { struct rds_sock *rs; - struct hlist_head *head = hash_to_bucket(addr, port); + struct hlist_head *head = &bucket->head; u64 cmp; u64 needle = ((u64)be32_to_cpu(addr) << 32) | be16_to_cpu(port); @@ -91,10 +96,11 @@ struct rds_sock *rds_find_bound(__be32 addr, __be16 port) { struct rds_sock *rs; unsigned long flags; + struct bind_bucket *bucket = hash_to_bucket(addr, port); - read_lock_irqsave(&rds_bind_lock, flags); - rs = rds_bind_lookup(addr, port, NULL); - read_unlock_irqrestore(&rds_bind_lock, flags); + read_lock_irqsave(&bucket->lock, flags); + rs = rds_bind_lookup(bucket, addr, port, NULL); + read_unlock_irqrestore(&bucket->lock, flags); if (rs && sock_flag(rds_rs_to_sk(rs), SOCK_DEAD)) { rds_sock_put(rs); @@ -113,6 +119,7 @@ static int rds_add_bound(struct rds_sock *rs, __be32 addr, __be16 *port) unsigned long flags; int ret = -EADDRINUSE; u16 rover, last; + struct bind_bucket *bucket; if (*port != 0) { rover = be16_to_cpu(*port); @@ -122,13 +129,15 @@ static int rds_add_bound(struct rds_sock *rs, __be32 addr, __be16 *port) last = rover - 1; } - write_lock_irqsave(&rds_bind_lock, flags); - do { struct rds_sock *rrs; if (rover == 0) rover++; - rrs = rds_bind_lookup(addr, cpu_to_be16(rover), rs); + + bucket = hash_to_bucket(addr, cpu_to_be16(rover)); + write_lock_irqsave(&bucket->lock, flags); + rrs = rds_bind_lookup(bucket, addr, cpu_to_be16(rover), rs); + write_unlock_irqrestore(&bucket->lock, flags); if (!rrs) { *port = rs->rs_bound_port; ret = 0; @@ -140,16 +149,16 @@ static int rds_add_bound(struct rds_sock *rs, __be32 addr, __be16 *port) } } while (rover++ != last); - write_unlock_irqrestore(&rds_bind_lock, flags); - return ret; } void rds_remove_bound(struct rds_sock *rs) { unsigned long flags; + struct bind_bucket *bucket = + hash_to_bucket(rs->rs_bound_addr, rs->rs_bound_port); - write_lock_irqsave(&rds_bind_lock, flags); + write_lock_irqsave(&bucket->lock, flags); if (rs->rs_bound_addr) { rdsdebug("rs %p unbinding from %pI4:%d\n", @@ -161,7 +170,7 @@ void rds_remove_bound(struct rds_sock *rs) rs->rs_bound_addr = 0; } - write_unlock_irqrestore(&rds_bind_lock, flags); + write_unlock_irqrestore(&bucket->lock, flags); } int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) @@ -207,3 +216,11 @@ out: release_sock(sk); return ret; } + +void rds_bind_lock_init(void) +{ + int i; + + for (i = 0; i < BIND_HASH_SIZE; i++) + rwlock_init(&bind_hash_table[i].lock); +} diff --git a/net/rds/rds.h b/net/rds/rds.h index afb4048..121fb81 100644 --- a/net/rds/rds.h +++ b/net/rds/rds.h @@ -603,6 +603,7 @@ extern wait_queue_head_t rds_poll_waitq; int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len); void rds_remove_bound(struct rds_sock *rs); struct rds_sock *rds_find_bound(__be32 addr, __be16 port); +void rds_bind_lock_init(void); /* cong.c */ int rds_cong_get_maps(struct rds_connection *conn); -- cgit v0.10.2 From 23270e160dd5d240dbf73722c1df9ff3044d7bc9 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Wed, 30 Sep 2015 16:28:52 +0300 Subject: Bluetooth: hci_intel: Cleanup the device probe code There is some unneeded code in "hci_intel" probing. First acpi_match_device() call is needless as driver/platform/acpi core code has already done the matching before calling the probe and the driver does not use the returned pointer to matching _HID other than checking is it NULL. Then tree wide grep for "hci_intel" doesn't reveal that there is any code registering this platform device so it looks this device is always backed with ACPI companion so also ACPI_HANDLE() test can be removed. Signed-off-by: Jarkko Nikula Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c index 49e2540..2952107 100644 --- a/drivers/bluetooth/hci_intel.c +++ b/drivers/bluetooth/hci_intel.c @@ -1165,22 +1165,6 @@ static const struct acpi_device_id intel_acpi_match[] = { { }, }; MODULE_DEVICE_TABLE(acpi, intel_acpi_match); - -static int intel_acpi_probe(struct intel_device *idev) -{ - const struct acpi_device_id *id; - - id = acpi_match_device(intel_acpi_match, &idev->pdev->dev); - if (!id) - return -ENODEV; - - return 0; -} -#else -static int intel_acpi_probe(struct intel_device *idev) -{ - return -ENODEV; -} #endif #ifdef CONFIG_PM @@ -1248,14 +1232,6 @@ static int intel_probe(struct platform_device *pdev) idev->pdev = pdev; - if (ACPI_HANDLE(&pdev->dev)) { - int err = intel_acpi_probe(idev); - if (err) - return err; - } else { - return -ENODEV; - } - idev->reset = devm_gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(idev->reset)) { -- cgit v0.10.2 From 09dbf1b7847ca09afb4616021fdc928452511888 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Wed, 30 Sep 2015 16:26:41 +0300 Subject: Bluetooth: hci_bcm: Add missing acpi_dev_free_resource_list() Caller of acpi_dev_get_resources() should free the constructed resource list by calling the acpi_dev_free_resource_list() in order to avoid memory leak. Signed-off-by: Jarkko Nikula Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 0c791ac..1a538ad 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -706,6 +706,7 @@ static int bcm_acpi_probe(struct bcm_device *dev) return 0; acpi_dev_get_resources(adev, &resources, bcm_resource, dev); + acpi_dev_free_resource_list(&resources); dmi_id = dmi_first_match(bcm_wrong_irq_dmi_table); if (dmi_id) { -- cgit v0.10.2 From 5be00284dc85dadd5241833fbca645c19baebebb Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Wed, 30 Sep 2015 16:26:42 +0300 Subject: Bluetooth: hci_bcm: Handle possible error from acpi_dev_get_resources() Driver doesn't handle possible error from acpi_dev_get_resources(). Test it and return the error code in case of error. Signed-off-by: Jarkko Nikula Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 1a538ad..5375c9c 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -705,7 +705,9 @@ static int bcm_acpi_probe(struct bcm_device *dev) if (!adev) return 0; - acpi_dev_get_resources(adev, &resources, bcm_resource, dev); + ret = acpi_dev_get_resources(adev, &resources, bcm_resource, dev); + if (ret < 0) + return ret; acpi_dev_free_resource_list(&resources); dmi_id = dmi_first_match(bcm_wrong_irq_dmi_table); -- cgit v0.10.2 From 5fbae60d43652edc005f1a690345ec7e290def8e Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Wed, 30 Sep 2015 16:26:43 +0300 Subject: Bluetooth: hci_bcm: Remove needless acpi_match_device() call There is no need to call acpi_match_device() in driver's probe path and verify does it find a match to given ACPI _HIDs in .acpi_match_table as driver/platform/acpi core code has found the match prior calling the probe. Signed-off-by: Jarkko Nikula Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 5375c9c..a1b9bbc 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -647,16 +647,11 @@ static int bcm_resource(struct acpi_resource *ares, void *data) static int bcm_acpi_probe(struct bcm_device *dev) { struct platform_device *pdev = dev->pdev; - const struct acpi_device_id *id; struct acpi_device *adev; LIST_HEAD(resources); const struct dmi_system_id *dmi_id; int ret; - id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev); - if (!id) - return -ENODEV; - /* Retrieve GPIO data */ dev->name = dev_name(&pdev->dev); ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(&pdev->dev), -- cgit v0.10.2 From 4d1c4558028ff0b7dbc1e24c319f478658f0620d Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Wed, 30 Sep 2015 16:26:44 +0300 Subject: Bluetooth: hci_bcm: Remove needless looking code Tree wide grep for "hci_bcm" doesn't reveal there is any code registering this platform device and "struct acpi_device_id" use for passing the platform data looks a debug/test code leftover to me. I'm assuming this driver effectively supports only ACPI enumeration and thus test for ACPI_HANDLE() and platform data can be removed. Signed-off-by: Jarkko Nikula Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index a1b9bbc..967d166 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -724,7 +724,6 @@ static int bcm_acpi_probe(struct bcm_device *dev) static int bcm_probe(struct platform_device *pdev) { struct bcm_device *dev; - struct acpi_device_id *pdata = pdev->dev.platform_data; int ret; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); @@ -733,15 +732,9 @@ static int bcm_probe(struct platform_device *pdev) dev->pdev = pdev; - if (ACPI_HANDLE(&pdev->dev)) { - ret = bcm_acpi_probe(dev); - if (ret) - return ret; - } else if (pdata) { - dev->name = pdata->id; - } else { - return -ENODEV; - } + ret = bcm_acpi_probe(dev); + if (ret) + return ret; platform_set_drvdata(pdev, dev); -- cgit v0.10.2 From e98d6d6203552ebd1e80ac17de857547efa37fba Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Wed, 30 Sep 2015 16:26:45 +0300 Subject: Bluetooth: hci_bcm: Do not test ACPI companion in bcm_acpi_probe() This device has always ACPI companion because driver supports only ACPI enumeration. Therefore there is no need to test it in bcm_acpi_probe() and we can pass it directly to acpi_dev_get_resources() (which will return -EINVAL in case of NULL argument is passed). Signed-off-by: Jarkko Nikula Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 967d166..5128732 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -647,7 +647,6 @@ static int bcm_resource(struct acpi_resource *ares, void *data) static int bcm_acpi_probe(struct bcm_device *dev) { struct platform_device *pdev = dev->pdev; - struct acpi_device *adev; LIST_HEAD(resources); const struct dmi_system_id *dmi_id; int ret; @@ -696,11 +695,8 @@ static int bcm_acpi_probe(struct bcm_device *dev) } /* Retrieve UART ACPI info */ - adev = ACPI_COMPANION(&dev->pdev->dev); - if (!adev) - return 0; - - ret = acpi_dev_get_resources(adev, &resources, bcm_resource, dev); + ret = acpi_dev_get_resources(ACPI_COMPANION(&dev->pdev->dev), + &resources, bcm_resource, dev); if (ret < 0) return ret; acpi_dev_free_resource_list(&resources); -- cgit v0.10.2 From 5f509239eccc9d118d3474a22e78b3da1ceefe02 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Thu, 1 Oct 2015 08:03:06 +0200 Subject: ieee802154: handle datagram variables as u16 This reverts commit 9abc378c66e3d6f437eed77c1c534cbc183523f7 ("ieee802154: 6lowpan: change datagram var types"). The reason is that I forgot the IPv6 fragmentation here. Our MTU of lowpan interface is 1280 and skb->len should not above of that. If we reach a payload above 1280 in IPv6 header then we have a IPv6 fragmentation above 802.15.4 6LoWPAN fragmentation. The type "u16" was fine, instead I added now a WARN_ON_ONCE if skb->len is above MTU which should never happen otherwise IPv6 on minimum MTU size is broken. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/ieee802154/6lowpan/6lowpan_i.h b/net/ieee802154/6lowpan/6lowpan_i.h index 10d44d0..b4e17a7 100644 --- a/net/ieee802154/6lowpan/6lowpan_i.h +++ b/net/ieee802154/6lowpan/6lowpan_i.h @@ -18,7 +18,7 @@ typedef unsigned __bitwise__ lowpan_rx_result; struct lowpan_create_arg { u16 tag; - unsigned int d_size; + u16 d_size; const struct ieee802154_addr *src; const struct ieee802154_addr *dst; }; @@ -29,7 +29,7 @@ struct lowpan_frag_queue { struct inet_frag_queue q; u16 tag; - unsigned int d_size; + u16 d_size; struct ieee802154_addr saddr; struct ieee802154_addr daddr; }; diff --git a/net/ieee802154/6lowpan/reassembly.c b/net/ieee802154/6lowpan/reassembly.c index af663cb..12e8cf4 100644 --- a/net/ieee802154/6lowpan/reassembly.c +++ b/net/ieee802154/6lowpan/reassembly.c @@ -37,7 +37,7 @@ static struct inet_frags lowpan_frags; static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *prev, struct net_device *ldev); -static unsigned int lowpan_hash_frag(u16 tag, unsigned int d_size, +static unsigned int lowpan_hash_frag(u16 tag, u16 d_size, const struct ieee802154_addr *saddr, const struct ieee802154_addr *daddr) { diff --git a/net/ieee802154/6lowpan/tx.c b/net/ieee802154/6lowpan/tx.c index 5736302..62a21f6 100644 --- a/net/ieee802154/6lowpan/tx.c +++ b/net/ieee802154/6lowpan/tx.c @@ -137,8 +137,8 @@ lowpan_xmit_fragment(struct sk_buff *skb, const struct ieee802154_hdr *wpan_hdr, static int lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *ldev, - const struct ieee802154_hdr *wpan_hdr, - unsigned int dgram_size, unsigned int dgram_offset) + const struct ieee802154_hdr *wpan_hdr, u16 dgram_size, + u16 dgram_offset) { __be16 frag_tag; u8 frag_hdr[5]; @@ -203,7 +203,7 @@ err: } static int lowpan_header(struct sk_buff *skb, struct net_device *ldev, - unsigned int *dgram_size, unsigned int *dgram_offset) + u16 *dgram_size, u16 *dgram_offset) { struct wpan_dev *wpan_dev = lowpan_dev_info(ldev)->wdev->ieee802154_ptr; struct ieee802154_addr sa, da; @@ -253,10 +253,12 @@ netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *ldev) { struct ieee802154_hdr wpan_hdr; int max_single, ret; - unsigned int dgram_size, dgram_offset; + u16 dgram_size, dgram_offset; pr_debug("package xmit\n"); + WARN_ON_ONCE(skb->len > IPV6_MIN_MTU); + /* We must take a copy of the skb before we modify/replace the ipv6 * header as the header could be used elsewhere */ -- cgit v0.10.2 From b1842ffddf8941aee4fcd95594bf62d3dc2867cc Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 1 Oct 2015 11:41:42 -0500 Subject: ipv6: Add missing newline to __xfrm6_output_finish Add a newline between variable declarations and the code. Signed-off-by: "Eric W. Biederman" diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c index 335066a..4cefda0 100644 --- a/net/ipv6/xfrm6_output.c +++ b/net/ipv6/xfrm6_output.c @@ -134,6 +134,7 @@ int xfrm6_output_finish(struct sock *sk, struct sk_buff *skb) static int __xfrm6_output_finish(struct net *net, struct sock *sk, struct sk_buff *skb) { struct xfrm_state *x = skb_dst(skb)->xfrm; + return x->outer_mode->afinfo->output_finish(sk, skb); } -- cgit v0.10.2 From aa6555622cdf443f0b001352fdc3afb6e7bce20d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 2 Oct 2015 10:47:29 +0300 Subject: nl802154: Missing return in nl802154_add_llsec_key() There was a missing return here so it meant that often ieee802154_llsec_parse_key_id() was not called. Fixes: a26c5fd7622d ('nl802154: add support for security layer') Signed-off-by: Dan Carpenter Acked-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c index 1e9e865..16ef0d9 100644 --- a/net/ieee802154/nl802154.c +++ b/net/ieee802154/nl802154.c @@ -1534,6 +1534,7 @@ static int nl802154_add_llsec_key(struct sk_buff *skb, struct genl_info *info) if (!attrs[NL802154_KEY_ATTR_USAGE_FRAMES] || !attrs[NL802154_KEY_ATTR_BYTES]) + return -EINVAL; if (ieee802154_llsec_parse_key_id(attrs[NL802154_KEY_ATTR_ID], &id) < 0) return -ENOBUFS; -- cgit v0.10.2 From d06f107bcd828a6c3ecd4a7d449d5d0c0dba0326 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Thu, 1 Oct 2015 18:16:21 +0200 Subject: Bluetooth: btintel: Add iBT register access over HCI support Add regmap ibt to support Intel Bluetooth silicon register access over HCI. Intel BT/FM combo chip allows to read/write some registers (e.g. FM registers) via its HCI interface. Read/Write operations are performed via a HCI transaction composed of a HCI command (host->controller) followed by a HCI command complete event (controller->host). Read/Write Command opcodes can be specified to the regmap init function. We define data formats which are intel/vendor specific. Register Read/Write HCI command payload (Host): Field: | REG ADDR | MODE | DATA_LEN | DATA... | size: | 32b | 8b | 8b | 8b* | Register Read HCI command complete event payload (Controller): Field: | CMD STATUS | REG ADDR | DATA... | size: | 8b | 32b | 8b* | Register Write HCI command complete event payload (Controller): Field: | CMD_STATUS | size: | 8b | Since this payload is HCI encapsulated, Little Endian byte order is used. Write/Read Example: If we write 0x0000002a at address 0x00008c04, with opcode_write 0xfc5d, The resulting transaction is (btmon trace): < HCI Command (0x3f|0x005d) plen 10 [hci0] 04 8c 00 00 02 04 2a 00 00 00 > HCI Event (0x0e) plen 4 Unknown (0x3f|0x005d) ncmd 1 00 Then, if we read the same register with opcode_read 0xfc5e: < HCI Command (0x3f|0x005e) plen 6 [hci0] 04 8c 00 00 02 04 > HCI Event (0x0e) plen 12 [hci0] Unknown (0x3f|0x005e) ncmd 1 00 04 8c 00 00 2a 00 00 00 Signed-off-by: Loic Poulain Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 3d480d8..6299954 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -4,6 +4,7 @@ menu "Bluetooth device drivers" config BT_INTEL tristate + select REGMAP config BT_BCM tristate diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index 9e18988..7047fe6 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -215,6 +216,201 @@ int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name) } EXPORT_SYMBOL_GPL(btintel_load_ddc_config); +/* ------- REGMAP IBT SUPPORT ------- */ + +#define IBT_REG_MODE_8BIT 0x00 +#define IBT_REG_MODE_16BIT 0x01 +#define IBT_REG_MODE_32BIT 0x02 + +struct regmap_ibt_context { + struct hci_dev *hdev; + __u16 op_write; + __u16 op_read; +}; + +struct ibt_cp_reg_access { + __le32 addr; + __u8 mode; + __u8 len; + __u8 data[0]; +} __packed; + +struct ibt_rp_reg_access { + __u8 status; + __le32 addr; + __u8 data[0]; +} __packed; + +static int regmap_ibt_read(void *context, const void *addr, size_t reg_size, + void *val, size_t val_size) +{ + struct regmap_ibt_context *ctx = context; + struct ibt_cp_reg_access cp; + struct ibt_rp_reg_access *rp; + struct sk_buff *skb; + int err = 0; + + if (reg_size != sizeof(__le32)) + return -EINVAL; + + switch (val_size) { + case 1: + cp.mode = IBT_REG_MODE_8BIT; + break; + case 2: + cp.mode = IBT_REG_MODE_16BIT; + break; + case 4: + cp.mode = IBT_REG_MODE_32BIT; + break; + default: + return -EINVAL; + } + + /* regmap provides a little-endian formatted addr */ + cp.addr = *(__le32 *)addr; + cp.len = val_size; + + bt_dev_dbg(ctx->hdev, "Register (0x%x) read", le32_to_cpu(cp.addr)); + + skb = hci_cmd_sync(ctx->hdev, ctx->op_read, sizeof(cp), &cp, + HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + bt_dev_err(ctx->hdev, "regmap: Register (0x%x) read error (%d)", + le32_to_cpu(cp.addr), err); + return err; + } + + if (skb->len != sizeof(*rp) + val_size) { + bt_dev_err(ctx->hdev, "regmap: Register (0x%x) read error, bad len", + le32_to_cpu(cp.addr)); + err = -EINVAL; + goto done; + } + + rp = (struct ibt_rp_reg_access *)skb->data; + + if (rp->addr != cp.addr) { + bt_dev_err(ctx->hdev, "regmap: Register (0x%x) read error, bad addr", + le32_to_cpu(rp->addr)); + err = -EINVAL; + goto done; + } + + memcpy(val, rp->data, val_size); + +done: + kfree_skb(skb); + return err; +} + +static int regmap_ibt_gather_write(void *context, + const void *addr, size_t reg_size, + const void *val, size_t val_size) +{ + struct regmap_ibt_context *ctx = context; + struct ibt_cp_reg_access *cp; + struct sk_buff *skb; + int plen = sizeof(*cp) + val_size; + u8 mode; + int err = 0; + + if (reg_size != sizeof(__le32)) + return -EINVAL; + + switch (val_size) { + case 1: + mode = IBT_REG_MODE_8BIT; + break; + case 2: + mode = IBT_REG_MODE_16BIT; + break; + case 4: + mode = IBT_REG_MODE_32BIT; + break; + default: + return -EINVAL; + } + + cp = kmalloc(plen, GFP_KERNEL); + if (!cp) + return -ENOMEM; + + /* regmap provides a little-endian formatted addr/value */ + cp->addr = *(__le32 *)addr; + cp->mode = mode; + cp->len = val_size; + memcpy(&cp->data, val, val_size); + + bt_dev_dbg(ctx->hdev, "Register (0x%x) write", le32_to_cpu(cp->addr)); + + skb = hci_cmd_sync(ctx->hdev, ctx->op_write, plen, cp, HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + bt_dev_err(ctx->hdev, "regmap: Register (0x%x) write error (%d)", + le32_to_cpu(cp->addr), err); + goto done; + } + kfree_skb(skb); + +done: + kfree(cp); + return err; +} + +static int regmap_ibt_write(void *context, const void *data, size_t count) +{ + /* data contains register+value, since we only support 32bit addr, + * minimum data size is 4 bytes. + */ + if (WARN_ONCE(count < 4, "Invalid register access")) + return -EINVAL; + + return regmap_ibt_gather_write(context, data, 4, data + 4, count - 4); +} + +static void regmap_ibt_free_context(void *context) +{ + kfree(context); +} + +static struct regmap_bus regmap_ibt = { + .read = regmap_ibt_read, + .write = regmap_ibt_write, + .gather_write = regmap_ibt_gather_write, + .free_context = regmap_ibt_free_context, + .reg_format_endian_default = REGMAP_ENDIAN_LITTLE, + .val_format_endian_default = REGMAP_ENDIAN_LITTLE, +}; + +/* Config is the same for all register regions */ +static const struct regmap_config regmap_ibt_cfg = { + .name = "btintel_regmap", + .reg_bits = 32, + .val_bits = 32, +}; + +struct regmap *btintel_regmap_init(struct hci_dev *hdev, u16 opcode_read, + u16 opcode_write) +{ + struct regmap_ibt_context *ctx; + + bt_dev_info(hdev, "regmap: Init R%x-W%x region", opcode_read, + opcode_write); + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return ERR_PTR(-ENOMEM); + + ctx->op_read = opcode_read; + ctx->op_write = opcode_write; + ctx->hdev = hdev; + + return regmap_init(&hdev->dev, ®map_ibt, ctx, ®map_ibt_cfg); +} +EXPORT_SYMBOL_GPL(btintel_regmap_init); + MODULE_AUTHOR("Marcel Holtmann "); MODULE_DESCRIPTION("Bluetooth support for Intel devices ver " VERSION); MODULE_VERSION(VERSION); diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h index 52deaf2..f0655c4 100644 --- a/drivers/bluetooth/btintel.h +++ b/drivers/bluetooth/btintel.h @@ -80,6 +80,9 @@ int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, u32 plen, const void *param); int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name); +struct regmap *btintel_regmap_init(struct hci_dev *hdev, u16 opcode_read, + u16 opcode_write); + #else static inline int btintel_check_bdaddr(struct hci_dev *hdev) @@ -113,4 +116,10 @@ static inline int btintel_load_ddc_config(struct hci_dev *hdev, return -EOPNOTSUPP; } +static inline struct regmap *btintel_regmap_init(struct hci_dev *hdev, + u16 opcode_read, + u16 opcode_write) +{ + return ERR_PTR(-EINVAL); +} #endif -- cgit v0.10.2 From 8dc5562e4e2b8255d2ea5d472a7fa25d5aa20da4 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Mon, 17 Aug 2015 11:19:02 +0800 Subject: i40e: Use numa_mem_id() to better support memoryless node Function i40e_clean_rx_irq() tries to reuse memory pages allocated from the nearest node. To better support memoryless node, use numa_mem_id() instead of numa_node_id() to get the nearest node with memory. This change should only affect performance. Signed-off-by: Jiang Liu Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 0d692dd..85e61b0 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1443,7 +1443,7 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget) unsigned int total_rx_bytes = 0, total_rx_packets = 0; u16 rx_packet_len, rx_header_len, rx_sph, rx_hbo; u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); - const int current_node = numa_node_id(); + const int current_node = numa_mem_id(); struct i40e_vsi *vsi = rx_ring->vsi; u16 i = rx_ring->next_to_clean; union i40e_rx_desc *rx_desc; -- cgit v0.10.2 From 27ca2753500537b8f3f3aba43558d68e2e8439a1 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Mon, 17 Aug 2015 11:19:03 +0800 Subject: i40evf: Use numa_mem_id() to better support memoryless node Function i40e_clean_rx_irq() tries to reuse memory pages allocated from the nearest node. To better support memoryless node, use numa_mem_id() instead of numa_node_id() to get the nearest node with memory. This change should only affect performance. Signed-off-by: Jiang Liu Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index aaee89f..0e1a4d6 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -917,7 +917,7 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget) unsigned int total_rx_bytes = 0, total_rx_packets = 0; u16 rx_packet_len, rx_header_len, rx_sph, rx_hbo; u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); - const int current_node = numa_node_id(); + const int current_node = numa_mem_id(); struct i40e_vsi *vsi = rx_ring->vsi; u16 i = rx_ring->next_to_clean; union i40e_rx_desc *rx_desc; -- cgit v0.10.2 From 126b63d9d3aa99a90381ed168a885989dd33cf15 Mon Sep 17 00:00:00 2001 From: Anjali Singhai Date: Mon, 24 Aug 2015 13:26:53 -0700 Subject: i40e: Fix a memory leak in X722 rss config path In any case free the memory allocated before exiting. Reported-by: Dan Carpenter Signed-off-by: Anjali Singhai Jain Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 4345fc5..5646ee8 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -7625,7 +7625,7 @@ static int i40e_config_rss_aq(struct i40e_vsi *vsi, const u8 *seed) "Cannot set RSS key, err %s aq_err %s\n", i40e_stat_str(&pf->hw, ret), i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); - return ret; + goto config_rss_aq_out; } if (vsi->type == I40E_VSI_MAIN) @@ -7639,6 +7639,8 @@ static int i40e_config_rss_aq(struct i40e_vsi *vsi, const u8 *seed) i40e_stat_str(&pf->hw, ret), i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); +config_rss_aq_out: + kfree(rss_lut); return ret; } -- cgit v0.10.2 From 24408e7ae677cea4e44ce6869a1b803820839471 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Mon, 28 Sep 2015 14:12:38 -0400 Subject: i40e/i40evf: grab the AQ spinlocks before clearing registers Make sure we have the spinlocks before we clear the ARQ and ASQ management registers. Also, widen the locked portion and make a sanity check earlier in the send function to be sure we're working with safe register values. Change-ID: I34b56044b33461ed780f3d2de8d62826cdf933f9 Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.c b/drivers/net/ethernet/intel/i40e/i40e_adminq.c index e59ffb2..34a64e0 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq.c +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.c @@ -482,8 +482,12 @@ static i40e_status i40e_shutdown_asq(struct i40e_hw *hw) { i40e_status ret_code = 0; - if (hw->aq.asq.count == 0) - return I40E_ERR_NOT_READY; + mutex_lock(&hw->aq.asq_mutex); + + if (hw->aq.asq.count == 0) { + ret_code = I40E_ERR_NOT_READY; + goto shutdown_asq_out; + } /* Stop firmware AdminQ processing */ wr32(hw, hw->aq.asq.head, 0); @@ -492,16 +496,13 @@ static i40e_status i40e_shutdown_asq(struct i40e_hw *hw) wr32(hw, hw->aq.asq.bal, 0); wr32(hw, hw->aq.asq.bah, 0); - /* make sure lock is available */ - mutex_lock(&hw->aq.asq_mutex); - hw->aq.asq.count = 0; /* to indicate uninitialized queue */ /* free ring buffers */ i40e_free_asq_bufs(hw); +shutdown_asq_out: mutex_unlock(&hw->aq.asq_mutex); - return ret_code; } @@ -515,8 +516,12 @@ static i40e_status i40e_shutdown_arq(struct i40e_hw *hw) { i40e_status ret_code = 0; - if (hw->aq.arq.count == 0) - return I40E_ERR_NOT_READY; + mutex_lock(&hw->aq.arq_mutex); + + if (hw->aq.arq.count == 0) { + ret_code = I40E_ERR_NOT_READY; + goto shutdown_arq_out; + } /* Stop firmware AdminQ processing */ wr32(hw, hw->aq.arq.head, 0); @@ -525,16 +530,13 @@ static i40e_status i40e_shutdown_arq(struct i40e_hw *hw) wr32(hw, hw->aq.arq.bal, 0); wr32(hw, hw->aq.arq.bah, 0); - /* make sure lock is available */ - mutex_lock(&hw->aq.arq_mutex); - hw->aq.arq.count = 0; /* to indicate uninitialized queue */ /* free ring buffers */ i40e_free_arq_bufs(hw); +shutdown_arq_out: mutex_unlock(&hw->aq.arq_mutex); - return ret_code; } @@ -745,19 +747,21 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw, u16 retval = 0; u32 val = 0; - val = rd32(hw, hw->aq.asq.head); - if (val >= hw->aq.num_asq_entries) { + mutex_lock(&hw->aq.asq_mutex); + + if (hw->aq.asq.count == 0) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, - "AQTX: head overrun at %d\n", val); + "AQTX: Admin queue not initialized.\n"); status = I40E_ERR_QUEUE_EMPTY; - goto asq_send_command_exit; + goto asq_send_command_error; } - if (hw->aq.asq.count == 0) { + val = rd32(hw, hw->aq.asq.head); + if (val >= hw->aq.num_asq_entries) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, - "AQTX: Admin queue not initialized.\n"); + "AQTX: head overrun at %d\n", val); status = I40E_ERR_QUEUE_EMPTY; - goto asq_send_command_exit; + goto asq_send_command_error; } details = I40E_ADMINQ_DETAILS(hw->aq.asq, hw->aq.asq.next_to_use); @@ -782,8 +786,6 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw, desc->flags &= ~cpu_to_le16(details->flags_dis); desc->flags |= cpu_to_le16(details->flags_ena); - mutex_lock(&hw->aq.asq_mutex); - if (buff_size > hw->aq.asq_buf_size) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, @@ -907,7 +909,6 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw, asq_send_command_error: mutex_unlock(&hw->aq.asq_mutex); -asq_send_command_exit: return status; } diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq.c b/drivers/net/ethernet/intel/i40evf/i40e_adminq.c index 5026773..c7f59e0 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq.c @@ -469,8 +469,12 @@ static i40e_status i40e_shutdown_asq(struct i40e_hw *hw) { i40e_status ret_code = 0; - if (hw->aq.asq.count == 0) - return I40E_ERR_NOT_READY; + mutex_lock(&hw->aq.asq_mutex); + + if (hw->aq.asq.count == 0) { + ret_code = I40E_ERR_NOT_READY; + goto shutdown_asq_out; + } /* Stop firmware AdminQ processing */ wr32(hw, hw->aq.asq.head, 0); @@ -479,16 +483,13 @@ static i40e_status i40e_shutdown_asq(struct i40e_hw *hw) wr32(hw, hw->aq.asq.bal, 0); wr32(hw, hw->aq.asq.bah, 0); - /* make sure lock is available */ - mutex_lock(&hw->aq.asq_mutex); - hw->aq.asq.count = 0; /* to indicate uninitialized queue */ /* free ring buffers */ i40e_free_asq_bufs(hw); +shutdown_asq_out: mutex_unlock(&hw->aq.asq_mutex); - return ret_code; } @@ -502,8 +503,12 @@ static i40e_status i40e_shutdown_arq(struct i40e_hw *hw) { i40e_status ret_code = 0; - if (hw->aq.arq.count == 0) - return I40E_ERR_NOT_READY; + mutex_lock(&hw->aq.arq_mutex); + + if (hw->aq.arq.count == 0) { + ret_code = I40E_ERR_NOT_READY; + goto shutdown_arq_out; + } /* Stop firmware AdminQ processing */ wr32(hw, hw->aq.arq.head, 0); @@ -512,16 +517,13 @@ static i40e_status i40e_shutdown_arq(struct i40e_hw *hw) wr32(hw, hw->aq.arq.bal, 0); wr32(hw, hw->aq.arq.bah, 0); - /* make sure lock is available */ - mutex_lock(&hw->aq.arq_mutex); - hw->aq.arq.count = 0; /* to indicate uninitialized queue */ /* free ring buffers */ i40e_free_arq_bufs(hw); +shutdown_arq_out: mutex_unlock(&hw->aq.arq_mutex); - return ret_code; } @@ -685,19 +687,21 @@ i40e_status i40evf_asq_send_command(struct i40e_hw *hw, u16 retval = 0; u32 val = 0; - val = rd32(hw, hw->aq.asq.head); - if (val >= hw->aq.num_asq_entries) { + mutex_lock(&hw->aq.asq_mutex); + + if (hw->aq.asq.count == 0) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, - "AQTX: head overrun at %d\n", val); + "AQTX: Admin queue not initialized.\n"); status = I40E_ERR_QUEUE_EMPTY; - goto asq_send_command_exit; + goto asq_send_command_error; } - if (hw->aq.asq.count == 0) { + val = rd32(hw, hw->aq.asq.head); + if (val >= hw->aq.num_asq_entries) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, - "AQTX: Admin queue not initialized.\n"); + "AQTX: head overrun at %d\n", val); status = I40E_ERR_QUEUE_EMPTY; - goto asq_send_command_exit; + goto asq_send_command_error; } details = I40E_ADMINQ_DETAILS(hw->aq.asq, hw->aq.asq.next_to_use); @@ -722,8 +726,6 @@ i40e_status i40evf_asq_send_command(struct i40e_hw *hw, desc->flags &= ~cpu_to_le16(details->flags_dis); desc->flags |= cpu_to_le16(details->flags_ena); - mutex_lock(&hw->aq.asq_mutex); - if (buff_size > hw->aq.asq_buf_size) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, @@ -848,7 +850,6 @@ i40e_status i40evf_asq_send_command(struct i40e_hw *hw, asq_send_command_error: mutex_unlock(&hw->aq.asq_mutex); -asq_send_command_exit: return status; } -- cgit v0.10.2 From 8552d85442dd814d733f67592627ea55f7cbfb01 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Mon, 31 Aug 2015 19:54:51 -0400 Subject: i40evf: don't blow away MAC address Under certain circumstances, we can get an extra VF_RESOURCES message from the PF driver at runtime. When this happens, we need to parse it because our VSI may have changed out from underneath us, and that will affect our relationship with the PF driver. However, parsing the resources message also blows away our current MAC address in the hardware struct, usually with all zeros. When this happens, the next time the interface is opened, it will have no MAC address and will a) not work and b) complain. Fix this issue by restoring the current MAC address from the netdev struct after we parse the resource message. Change-ID: I6cd1b624fc20432f81dc901166c8de195b8e0e65 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index d4eb1a5..fbf2a1c 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -756,6 +756,8 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter, sizeof(struct i40e_virtchnl_vsi_resource); memcpy(adapter->vf_res, msg, min(msglen, len)); i40e_vf_parse_hw_config(&adapter->hw, adapter->vf_res); + /* restore current mac address */ + ether_addr_copy(adapter->hw.mac.addr, netdev->dev_addr); i40evf_process_config(adapter); } break; -- cgit v0.10.2 From 586c2b573ee4c2c4ba03e16318a16614ebf876f8 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Fri, 2 Oct 2015 15:05:10 +0200 Subject: bridge: vlan: use rcu list for the ordered vlan list When I did the conversion to rhashtable I missed the required locking of one important user of the vlan list - br_get_link_af_size_filtered() which is called: br_ifinfo_notify() -> br_nlmsg_size() -> br_get_link_af_size_filtered() and the notifications can be sent without holding rtnl. Before this conversion the function relied on using rcu and since we already use rcu to destroy the vlans, we can simply migrate the list to use the rcu helpers. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index c64dcad..c318619 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -34,7 +34,7 @@ static int __get_num_vlan_infos(struct net_bridge_vlan_group *vg, pvid = br_get_pvid(vg); /* Count number of vlan infos */ - list_for_each_entry(v, &vg->vlan_list, vlist) { + list_for_each_entry_rcu(v, &vg->vlan_list, vlist) { flags = 0; /* only a context, bridge vlan not activated */ if (!br_vlan_should_use(v)) @@ -76,13 +76,19 @@ initvars: static int br_get_num_vlan_infos(struct net_bridge_vlan_group *vg, u32 filter_mask) { + int num_vlans; + if (!vg) return 0; if (filter_mask & RTEXT_FILTER_BRVLAN) return vg->num_vlans; - return __get_num_vlan_infos(vg, filter_mask); + rcu_read_lock(); + num_vlans = __get_num_vlan_infos(vg, filter_mask); + rcu_read_unlock(); + + return num_vlans; } static size_t br_get_link_af_size_filtered(const struct net_device *dev, diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 1a79e19..d97a55e 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -111,12 +111,12 @@ static void __vlan_add_list(struct net_bridge_vlan *v) else break; } - list_add(&v->vlist, hpos); + list_add_rcu(&v->vlist, hpos); } static void __vlan_del_list(struct net_bridge_vlan *v) { - list_del(&v->vlist); + list_del_rcu(&v->vlist); } static int __vlan_vid_del(struct net_device *dev, struct net_bridge *br, -- cgit v0.10.2 From f8ed289fab843fbc9251aa2f5c3d416f09b5fc7e Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Fri, 2 Oct 2015 15:05:11 +0200 Subject: bridge: vlan: use br_vlan_(get|put)_master to deal with refcounts Introduce br_vlan_(get|put)_master which take a reference (or create the master vlan first if it didn't exist) and drop a reference respectively. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index d97a55e..6e41fba 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -146,6 +146,40 @@ static int __vlan_vid_del(struct net_device *dev, struct net_bridge *br, return err; } +/* Returns a master vlan, if it didn't exist it gets created. In all cases a + * a reference is taken to the master vlan before returning. + */ +static struct net_bridge_vlan *br_vlan_get_master(struct net_bridge *br, u16 vid) +{ + struct net_bridge_vlan *masterv; + + masterv = br_vlan_find(br->vlgrp, vid); + if (!masterv) { + /* missing global ctx, create it now */ + if (br_vlan_add(br, vid, 0)) + return NULL; + masterv = br_vlan_find(br->vlgrp, vid); + if (WARN_ON(!masterv)) + return NULL; + } + atomic_inc(&masterv->refcnt); + + return masterv; +} + +static void br_vlan_put_master(struct net_bridge_vlan *masterv) +{ + if (!br_vlan_is_master(masterv)) + return; + + if (atomic_dec_and_test(&masterv->refcnt)) { + rhashtable_remove_fast(&masterv->br->vlgrp->vlan_hash, + &masterv->vnode, br_vlan_rht_params); + __vlan_del_list(masterv); + kfree_rcu(masterv, rcu); + } +} + /* This is the shared VLAN add function which works for both ports and bridge * devices. There are four possible calls to this function in terms of the * vlan entry type: @@ -196,16 +230,9 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags) goto out_filt; } - masterv = br_vlan_find(br->vlgrp, v->vid); - if (!masterv) { - /* missing global ctx, create it now */ - err = br_vlan_add(br, v->vid, 0); - if (err) - goto out_filt; - masterv = br_vlan_find(br->vlgrp, v->vid); - WARN_ON(!masterv); - } - atomic_inc(&masterv->refcnt); + masterv = br_vlan_get_master(br, v->vid); + if (!masterv) + goto out_filt; v->brvlan = masterv; } @@ -240,7 +267,7 @@ out_filt: if (p) { __vlan_vid_del(dev, br, v->vid); if (masterv) { - atomic_dec(&masterv->refcnt); + br_vlan_put_master(masterv); v->brvlan = NULL; } } @@ -289,12 +316,7 @@ static int __vlan_del(struct net_bridge_vlan *v) kfree_rcu(v, rcu); } - if (atomic_dec_and_test(&masterv->refcnt)) { - rhashtable_remove_fast(&masterv->br->vlgrp->vlan_hash, - &masterv->vnode, br_vlan_rht_params); - __vlan_del_list(masterv); - kfree_rcu(masterv, rcu); - } + br_vlan_put_master(masterv); out: return err; } -- cgit v0.10.2 From 2ffdf508d278d48ccb928238846df352db21f4eb Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Fri, 2 Oct 2015 15:05:12 +0200 Subject: bridge: vlan: drop master_flags from __vlan_add There's only one user now and we can include the flag directly. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 6e41fba..2c1fdf9 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -212,8 +212,6 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags) } if (p) { - u16 master_flags = flags; - /* Add VLAN to the device filter if it is supported. * This ensures tagged traffic enters the bridge when * promiscuous mode is disabled by br_manage_promisc(). @@ -224,8 +222,8 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags) /* need to work on the master vlan too */ if (flags & BRIDGE_VLAN_INFO_MASTER) { - master_flags |= BRIDGE_VLAN_INFO_BRENTRY; - err = br_vlan_add(br, v->vid, master_flags); + err = br_vlan_add(br, v->vid, flags | + BRIDGE_VLAN_INFO_BRENTRY); if (err) goto out_filt; } -- cgit v0.10.2 From 6be144f62f64c8a67e11b2f8b86c7bf390b87411 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Fri, 2 Oct 2015 15:05:13 +0200 Subject: bridge: vlan: use br_vlan_should_use to simplify __vlan_add/del The checks that lead to num_vlans change are always what br_vlan_should_use checks for, namely if the vlan is only a context or not and depending on that it's either not counted or counted as a real/used vlan respectively. Also give better explanation in br_vlan_should_use's comment. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 4ed8308..1ff6a0f 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -400,7 +400,7 @@ static inline bool br_vlan_is_brentry(const struct net_bridge_vlan *v) return v->flags & BRIDGE_VLAN_INFO_BRENTRY; } -/* check if we should use the vlan entry is usable */ +/* check if we should use the vlan entry, returns false if it's only context */ static inline bool br_vlan_should_use(const struct net_bridge_vlan *v) { if (br_vlan_is_master(v)) { diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 2c1fdf9..b879111 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -195,7 +195,7 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags) { struct net_bridge_vlan *masterv = NULL; struct net_bridge_port *p = NULL; - struct rhashtable *tbl; + struct net_bridge_vlan_group *vg; struct net_device *dev; struct net_bridge *br; int err; @@ -203,12 +203,12 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags) if (br_vlan_is_master(v)) { br = v->br; dev = br->dev; - tbl = &br->vlgrp->vlan_hash; + vg = br->vlgrp; } else { p = v->port; br = p->br; dev = p->dev; - tbl = &p->vlgrp->vlan_hash; + vg = p->vlgrp; } if (p) { @@ -234,32 +234,31 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags) v->brvlan = masterv; } - /* Add the dev mac only if it's a usable vlan */ + /* Add the dev mac and count the vlan only if it's usable */ if (br_vlan_should_use(v)) { err = br_fdb_insert(br, p, dev->dev_addr, v->vid); if (err) { br_err(br, "failed insert local address into bridge forwarding table\n"); goto out_filt; } + vg->num_vlans++; } - err = rhashtable_lookup_insert_fast(tbl, &v->vnode, br_vlan_rht_params); + err = rhashtable_lookup_insert_fast(&vg->vlan_hash, &v->vnode, + br_vlan_rht_params); if (err) goto out_fdb_insert; __vlan_add_list(v); __vlan_add_flags(v, flags); - if (br_vlan_is_master(v)) { - if (br_vlan_is_brentry(v)) - br->vlgrp->num_vlans++; - } else { - p->vlgrp->num_vlans++; - } out: return err; out_fdb_insert: - br_fdb_find_delete_local(br, p, br->dev->dev_addr, v->vid); + if (br_vlan_should_use(v)) { + br_fdb_find_delete_local(br, p, dev->dev_addr, v->vid); + vg->num_vlans--; + } out_filt: if (p) { @@ -278,15 +277,12 @@ static int __vlan_del(struct net_bridge_vlan *v) struct net_bridge_vlan *masterv = v; struct net_bridge_vlan_group *vg; struct net_bridge_port *p = NULL; - struct net_bridge *br; int err = 0; if (br_vlan_is_master(v)) { - br = v->br; vg = v->br->vlgrp; } else { p = v->port; - br = p->br; vg = v->port->vlgrp; masterv = v->brvlan; } @@ -298,13 +294,9 @@ static int __vlan_del(struct net_bridge_vlan *v) goto out; } - if (br_vlan_is_master(v)) { - if (br_vlan_is_brentry(v)) { - v->flags &= ~BRIDGE_VLAN_INFO_BRENTRY; - br->vlgrp->num_vlans--; - } - } else { - p->vlgrp->num_vlans--; + if (br_vlan_should_use(v)) { + v->flags &= ~BRIDGE_VLAN_INFO_BRENTRY; + vg->num_vlans--; } if (masterv != v) { -- cgit v0.10.2 From 7910228b6bb35f3c8e0bc72a8d84c29616cb1b90 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 4 Oct 2015 14:23:28 +0200 Subject: bridge: netlink: add group_fwd_mask support Add IFLA_BR_GROUP_FWD_MASK attribute to allow setting and retrieving the group_fwd_mask via netlink. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 3a5f263..f7f4806 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -232,6 +232,7 @@ enum { IFLA_BR_PRIORITY, IFLA_BR_VLAN_FILTERING, IFLA_BR_VLAN_PROTOCOL, + IFLA_BR_GROUP_FWD_MASK, __IFLA_BR_MAX, }; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index c318619..39b201a 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -764,6 +764,7 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = { [IFLA_BR_PRIORITY] = { .type = NLA_U16 }, [IFLA_BR_VLAN_FILTERING] = { .type = NLA_U8 }, [IFLA_BR_VLAN_PROTOCOL] = { .type = NLA_U16 }, + [IFLA_BR_GROUP_FWD_MASK] = { .type = NLA_U16 }, }; static int br_changelink(struct net_device *brdev, struct nlattr *tb[], @@ -829,6 +830,14 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[], } #endif + if (data[IFLA_BR_GROUP_FWD_MASK]) { + u16 fwd_mask = nla_get_u16(data[IFLA_BR_GROUP_FWD_MASK]); + + if (fwd_mask & BR_GROUPFWD_RESTRICTED) + return -EINVAL; + br->group_fwd_mask = fwd_mask; + } + return 0; } @@ -844,6 +853,7 @@ static size_t br_get_size(const struct net_device *brdev) #ifdef CONFIG_BRIDGE_VLAN_FILTERING nla_total_size(sizeof(__be16)) + /* IFLA_BR_VLAN_PROTOCOL */ #endif + nla_total_size(sizeof(u16)) + /* IFLA_BR_GROUP_FWD_MASK */ 0; } @@ -856,6 +866,7 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) u32 ageing_time = jiffies_to_clock_t(br->ageing_time); u32 stp_enabled = br->stp_enabled; u16 priority = (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1]; + u16 group_fwd_mask = br->group_fwd_mask; u8 vlan_enabled = br_vlan_enabled(br); if (nla_put_u32(skb, IFLA_BR_FORWARD_DELAY, forward_delay) || @@ -864,7 +875,8 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) nla_put_u32(skb, IFLA_BR_AGEING_TIME, ageing_time) || nla_put_u32(skb, IFLA_BR_STP_STATE, stp_enabled) || nla_put_u16(skb, IFLA_BR_PRIORITY, priority) || - nla_put_u8(skb, IFLA_BR_VLAN_FILTERING, vlan_enabled)) + nla_put_u8(skb, IFLA_BR_VLAN_FILTERING, vlan_enabled) || + nla_put_u16(skb, IFLA_BR_GROUP_FWD_MASK, group_fwd_mask)) return -EMSGSIZE; #ifdef CONFIG_BRIDGE_VLAN_FILTERING -- cgit v0.10.2 From 5127c81f84de0dd643d5840a2c7de571bc6aceb3 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 4 Oct 2015 14:23:29 +0200 Subject: bridge: netlink: export root id Add IFLA_BR_ROOT_ID and export br->designated_root via netlink. For this purpose add struct ifla_bridge_id that would represent struct bridge_id. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index f7f4806..0381437 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -233,11 +233,17 @@ enum { IFLA_BR_VLAN_FILTERING, IFLA_BR_VLAN_PROTOCOL, IFLA_BR_GROUP_FWD_MASK, + IFLA_BR_ROOT_ID, __IFLA_BR_MAX, }; #define IFLA_BR_MAX (__IFLA_BR_MAX - 1) +struct ifla_bridge_id { + __u8 prio[2]; + __u8 addr[6]; /* ETH_ALEN */ +}; + enum { BRIDGE_MODE_UNSPEC, BRIDGE_MODE_HAIRPIN, diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 39b201a..7a36924 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -854,6 +854,7 @@ static size_t br_get_size(const struct net_device *brdev) nla_total_size(sizeof(__be16)) + /* IFLA_BR_VLAN_PROTOCOL */ #endif nla_total_size(sizeof(u16)) + /* IFLA_BR_GROUP_FWD_MASK */ + nla_total_size(sizeof(struct ifla_bridge_id)) + /* IFLA_BR_ROOT_ID */ 0; } @@ -868,6 +869,11 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) u16 priority = (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1]; u16 group_fwd_mask = br->group_fwd_mask; u8 vlan_enabled = br_vlan_enabled(br); + struct ifla_bridge_id root_id; + + memset(&root_id, 0, sizeof(root_id)); + memcpy(root_id.prio, br->designated_root.prio, sizeof(root_id.prio)); + memcpy(root_id.addr, br->designated_root.addr, sizeof(root_id.addr)); if (nla_put_u32(skb, IFLA_BR_FORWARD_DELAY, forward_delay) || nla_put_u32(skb, IFLA_BR_HELLO_TIME, hello_time) || @@ -876,7 +882,8 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) nla_put_u32(skb, IFLA_BR_STP_STATE, stp_enabled) || nla_put_u16(skb, IFLA_BR_PRIORITY, priority) || nla_put_u8(skb, IFLA_BR_VLAN_FILTERING, vlan_enabled) || - nla_put_u16(skb, IFLA_BR_GROUP_FWD_MASK, group_fwd_mask)) + nla_put_u16(skb, IFLA_BR_GROUP_FWD_MASK, group_fwd_mask) || + nla_put(skb, IFLA_BR_ROOT_ID, sizeof(root_id), &root_id)) return -EMSGSIZE; #ifdef CONFIG_BRIDGE_VLAN_FILTERING -- cgit v0.10.2 From 7599a2201fc71cdca16a92d350f14cce8730e03f Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 4 Oct 2015 14:23:30 +0200 Subject: bridge: netlink: export bridge id Add IFLA_BR_BRIDGE_ID and export br->bridge_id via netlink. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 0381437..7484954 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -234,6 +234,7 @@ enum { IFLA_BR_VLAN_PROTOCOL, IFLA_BR_GROUP_FWD_MASK, IFLA_BR_ROOT_ID, + IFLA_BR_BRIDGE_ID, __IFLA_BR_MAX, }; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 7a36924..a63f944 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -855,6 +855,7 @@ static size_t br_get_size(const struct net_device *brdev) #endif nla_total_size(sizeof(u16)) + /* IFLA_BR_GROUP_FWD_MASK */ nla_total_size(sizeof(struct ifla_bridge_id)) + /* IFLA_BR_ROOT_ID */ + nla_total_size(sizeof(struct ifla_bridge_id)) + /* IFLA_BR_BRIDGE_ID */ 0; } @@ -869,11 +870,14 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) u16 priority = (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1]; u16 group_fwd_mask = br->group_fwd_mask; u8 vlan_enabled = br_vlan_enabled(br); - struct ifla_bridge_id root_id; + struct ifla_bridge_id root_id, bridge_id; + memset(&bridge_id, 0, sizeof(bridge_id)); memset(&root_id, 0, sizeof(root_id)); memcpy(root_id.prio, br->designated_root.prio, sizeof(root_id.prio)); memcpy(root_id.addr, br->designated_root.addr, sizeof(root_id.addr)); + memcpy(bridge_id.prio, br->bridge_id.prio, sizeof(bridge_id.prio)); + memcpy(bridge_id.addr, br->bridge_id.addr, sizeof(bridge_id.addr)); if (nla_put_u32(skb, IFLA_BR_FORWARD_DELAY, forward_delay) || nla_put_u32(skb, IFLA_BR_HELLO_TIME, hello_time) || @@ -883,7 +887,8 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) nla_put_u16(skb, IFLA_BR_PRIORITY, priority) || nla_put_u8(skb, IFLA_BR_VLAN_FILTERING, vlan_enabled) || nla_put_u16(skb, IFLA_BR_GROUP_FWD_MASK, group_fwd_mask) || - nla_put(skb, IFLA_BR_ROOT_ID, sizeof(root_id), &root_id)) + nla_put(skb, IFLA_BR_ROOT_ID, sizeof(root_id), &root_id) || + nla_put(skb, IFLA_BR_BRIDGE_ID, sizeof(bridge_id), &bridge_id)) return -EMSGSIZE; #ifdef CONFIG_BRIDGE_VLAN_FILTERING -- cgit v0.10.2 From 8762ba680fe8d41b444fc92f90ce7194b2b8303b Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 4 Oct 2015 14:23:31 +0200 Subject: bridge: netlink: export root port Add IFLA_BR_ROOT_PORT and export it via netlink. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 7484954..172268a 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -235,6 +235,7 @@ enum { IFLA_BR_GROUP_FWD_MASK, IFLA_BR_ROOT_ID, IFLA_BR_BRIDGE_ID, + IFLA_BR_ROOT_PORT, __IFLA_BR_MAX, }; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index a63f944..652db1c 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -856,6 +856,7 @@ static size_t br_get_size(const struct net_device *brdev) nla_total_size(sizeof(u16)) + /* IFLA_BR_GROUP_FWD_MASK */ nla_total_size(sizeof(struct ifla_bridge_id)) + /* IFLA_BR_ROOT_ID */ nla_total_size(sizeof(struct ifla_bridge_id)) + /* IFLA_BR_BRIDGE_ID */ + nla_total_size(sizeof(u16)) + /* IFLA_BR_ROOT_PORT */ 0; } @@ -888,7 +889,8 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) nla_put_u8(skb, IFLA_BR_VLAN_FILTERING, vlan_enabled) || nla_put_u16(skb, IFLA_BR_GROUP_FWD_MASK, group_fwd_mask) || nla_put(skb, IFLA_BR_ROOT_ID, sizeof(root_id), &root_id) || - nla_put(skb, IFLA_BR_BRIDGE_ID, sizeof(bridge_id), &bridge_id)) + nla_put(skb, IFLA_BR_BRIDGE_ID, sizeof(bridge_id), &bridge_id) || + nla_put_u16(skb, IFLA_BR_ROOT_PORT, br->root_port)) return -EMSGSIZE; #ifdef CONFIG_BRIDGE_VLAN_FILTERING -- cgit v0.10.2 From 684dd248bee8c73eadb90706123bf1494d3218b8 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 4 Oct 2015 14:23:32 +0200 Subject: bridge: netlink: export root path cost Add IFLA_BR_ROOT_PATH_COST and export it via netlink. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 172268a..7d7236d 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -236,6 +236,7 @@ enum { IFLA_BR_ROOT_ID, IFLA_BR_BRIDGE_ID, IFLA_BR_ROOT_PORT, + IFLA_BR_ROOT_PATH_COST, __IFLA_BR_MAX, }; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 652db1c..cd0488b 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -857,6 +857,7 @@ static size_t br_get_size(const struct net_device *brdev) nla_total_size(sizeof(struct ifla_bridge_id)) + /* IFLA_BR_ROOT_ID */ nla_total_size(sizeof(struct ifla_bridge_id)) + /* IFLA_BR_BRIDGE_ID */ nla_total_size(sizeof(u16)) + /* IFLA_BR_ROOT_PORT */ + nla_total_size(sizeof(u32)) + /* IFLA_BR_ROOT_PATH_COST */ 0; } @@ -890,7 +891,8 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) nla_put_u16(skb, IFLA_BR_GROUP_FWD_MASK, group_fwd_mask) || nla_put(skb, IFLA_BR_ROOT_ID, sizeof(root_id), &root_id) || nla_put(skb, IFLA_BR_BRIDGE_ID, sizeof(bridge_id), &bridge_id) || - nla_put_u16(skb, IFLA_BR_ROOT_PORT, br->root_port)) + nla_put_u16(skb, IFLA_BR_ROOT_PORT, br->root_port) || + nla_put_u32(skb, IFLA_BR_ROOT_PATH_COST, br->root_path_cost)) return -EMSGSIZE; #ifdef CONFIG_BRIDGE_VLAN_FILTERING -- cgit v0.10.2 From ed4163098e3090bb7b51421bde977e355275a554 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 4 Oct 2015 14:23:33 +0200 Subject: bridge: netlink: export topology_change and topology_change_detected Add IFLA_BR_TOPOLOGY_CHANGE and IFLA_BR_TOPOLOGY_CHANGE_DETECTED and export them via netlink. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 7d7236d..b00286c 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -237,6 +237,8 @@ enum { IFLA_BR_BRIDGE_ID, IFLA_BR_ROOT_PORT, IFLA_BR_ROOT_PATH_COST, + IFLA_BR_TOPOLOGY_CHANGE, + IFLA_BR_TOPOLOGY_CHANGE_DETECTED, __IFLA_BR_MAX, }; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index cd0488b..8bcaa51 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -858,6 +858,8 @@ static size_t br_get_size(const struct net_device *brdev) nla_total_size(sizeof(struct ifla_bridge_id)) + /* IFLA_BR_BRIDGE_ID */ nla_total_size(sizeof(u16)) + /* IFLA_BR_ROOT_PORT */ nla_total_size(sizeof(u32)) + /* IFLA_BR_ROOT_PATH_COST */ + nla_total_size(sizeof(u8)) + /* IFLA_BR_TOPOLOGY_CHANGE */ + nla_total_size(sizeof(u8)) + /* IFLA_BR_TOPOLOGY_CHANGE_DETECTED */ 0; } @@ -892,7 +894,10 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) nla_put(skb, IFLA_BR_ROOT_ID, sizeof(root_id), &root_id) || nla_put(skb, IFLA_BR_BRIDGE_ID, sizeof(bridge_id), &bridge_id) || nla_put_u16(skb, IFLA_BR_ROOT_PORT, br->root_port) || - nla_put_u32(skb, IFLA_BR_ROOT_PATH_COST, br->root_path_cost)) + nla_put_u32(skb, IFLA_BR_ROOT_PATH_COST, br->root_path_cost) || + nla_put_u8(skb, IFLA_BR_TOPOLOGY_CHANGE, br->topology_change) || + nla_put_u8(skb, IFLA_BR_TOPOLOGY_CHANGE_DETECTED, + br->topology_change_detected)) return -EMSGSIZE; #ifdef CONFIG_BRIDGE_VLAN_FILTERING -- cgit v0.10.2 From d76bd14e0f759040efc8ce142dd6d1f9eca33d39 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 4 Oct 2015 14:23:34 +0200 Subject: bridge: netlink: export all timers Export the following bridge timers (also exported via sysfs): IFLA_BR_HELLO_TIMER, IFLA_BR_TCN_TIMER, IFLA_BR_TOPOLOGY_CHANGE_TIMER, IFLA_BR_GC_TIMER via netlink. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index b00286c..a1e3328 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -239,6 +239,10 @@ enum { IFLA_BR_ROOT_PATH_COST, IFLA_BR_TOPOLOGY_CHANGE, IFLA_BR_TOPOLOGY_CHANGE_DETECTED, + IFLA_BR_HELLO_TIMER, + IFLA_BR_TCN_TIMER, + IFLA_BR_TOPOLOGY_CHANGE_TIMER, + IFLA_BR_GC_TIMER, __IFLA_BR_MAX, }; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 8bcaa51..755bfe0 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -860,12 +860,17 @@ static size_t br_get_size(const struct net_device *brdev) nla_total_size(sizeof(u32)) + /* IFLA_BR_ROOT_PATH_COST */ nla_total_size(sizeof(u8)) + /* IFLA_BR_TOPOLOGY_CHANGE */ nla_total_size(sizeof(u8)) + /* IFLA_BR_TOPOLOGY_CHANGE_DETECTED */ + nla_total_size(sizeof(u64)) + /* IFLA_BR_HELLO_TIMER */ + nla_total_size(sizeof(u64)) + /* IFLA_BR_TCN_TIMER */ + nla_total_size(sizeof(u64)) + /* IFLA_BR_TOPOLOGY_CHANGE_TIMER */ + nla_total_size(sizeof(u64)) + /* IFLA_BR_GC_TIMER */ 0; } static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) { struct net_bridge *br = netdev_priv(brdev); + u64 hello_timer, tcn_timer, topology_change_timer, gc_timer; u32 forward_delay = jiffies_to_clock_t(br->forward_delay); u32 hello_time = jiffies_to_clock_t(br->hello_time); u32 age_time = jiffies_to_clock_t(br->max_age); @@ -882,6 +887,10 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) memcpy(root_id.addr, br->designated_root.addr, sizeof(root_id.addr)); memcpy(bridge_id.prio, br->bridge_id.prio, sizeof(bridge_id.prio)); memcpy(bridge_id.addr, br->bridge_id.addr, sizeof(bridge_id.addr)); + hello_timer = br_timer_value(&br->hello_timer); + tcn_timer = br_timer_value(&br->tcn_timer); + topology_change_timer = br_timer_value(&br->topology_change_timer); + gc_timer = br_timer_value(&br->gc_timer); if (nla_put_u32(skb, IFLA_BR_FORWARD_DELAY, forward_delay) || nla_put_u32(skb, IFLA_BR_HELLO_TIME, hello_time) || @@ -897,7 +906,12 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) nla_put_u32(skb, IFLA_BR_ROOT_PATH_COST, br->root_path_cost) || nla_put_u8(skb, IFLA_BR_TOPOLOGY_CHANGE, br->topology_change) || nla_put_u8(skb, IFLA_BR_TOPOLOGY_CHANGE_DETECTED, - br->topology_change_detected)) + br->topology_change_detected) || + nla_put_u64(skb, IFLA_BR_HELLO_TIMER, hello_timer) || + nla_put_u64(skb, IFLA_BR_TCN_TIMER, tcn_timer) || + nla_put_u64(skb, IFLA_BR_TOPOLOGY_CHANGE_TIMER, + topology_change_timer) || + nla_put_u64(skb, IFLA_BR_GC_TIMER, gc_timer)) return -EMSGSIZE; #ifdef CONFIG_BRIDGE_VLAN_FILTERING -- cgit v0.10.2 From 111189abc5c3f0ea6f516a6c3e8d8c3a2cf391d9 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 4 Oct 2015 14:23:35 +0200 Subject: bridge: netlink: add group_addr support Add IFLA_BR_GROUP_ADDR attribute to allow setting and retrieving the group_addr via netlink. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index a1e3328..eaeaac1 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -243,6 +243,7 @@ enum { IFLA_BR_TCN_TIMER, IFLA_BR_TOPOLOGY_CHANGE_TIMER, IFLA_BR_GC_TIMER, + IFLA_BR_GROUP_ADDR, __IFLA_BR_MAX, }; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 755bfe0..a05a430 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -765,6 +765,8 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = { [IFLA_BR_VLAN_FILTERING] = { .type = NLA_U8 }, [IFLA_BR_VLAN_PROTOCOL] = { .type = NLA_U16 }, [IFLA_BR_GROUP_FWD_MASK] = { .type = NLA_U16 }, + [IFLA_BR_GROUP_ADDR] = { .type = NLA_BINARY, + .len = ETH_ALEN }, }; static int br_changelink(struct net_device *brdev, struct nlattr *tb[], @@ -838,6 +840,25 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[], br->group_fwd_mask = fwd_mask; } + if (data[IFLA_BR_GROUP_ADDR]) { + u8 new_addr[ETH_ALEN]; + + if (nla_len(data[IFLA_BR_GROUP_ADDR]) != ETH_ALEN) + return -EINVAL; + memcpy(new_addr, nla_data(data[IFLA_BR_GROUP_ADDR]), ETH_ALEN); + if (!is_link_local_ether_addr(new_addr)) + return -EINVAL; + if (new_addr[5] == 1 || /* 802.3x Pause address */ + new_addr[5] == 2 || /* 802.3ad Slow protocols */ + new_addr[5] == 3) /* 802.1X PAE address */ + return -EINVAL; + spin_lock_bh(&br->lock); + memcpy(br->group_addr, new_addr, sizeof(br->group_addr)); + spin_unlock_bh(&br->lock); + br->group_addr_set = true; + br_recalculate_fwd_mask(br); + } + return 0; } @@ -864,6 +885,7 @@ static size_t br_get_size(const struct net_device *brdev) nla_total_size(sizeof(u64)) + /* IFLA_BR_TCN_TIMER */ nla_total_size(sizeof(u64)) + /* IFLA_BR_TOPOLOGY_CHANGE_TIMER */ nla_total_size(sizeof(u64)) + /* IFLA_BR_GC_TIMER */ + nla_total_size(ETH_ALEN) + /* IFLA_BR_GROUP_ADDR */ 0; } @@ -911,7 +933,8 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) nla_put_u64(skb, IFLA_BR_TCN_TIMER, tcn_timer) || nla_put_u64(skb, IFLA_BR_TOPOLOGY_CHANGE_TIMER, topology_change_timer) || - nla_put_u64(skb, IFLA_BR_GC_TIMER, gc_timer)) + nla_put_u64(skb, IFLA_BR_GC_TIMER, gc_timer) || + nla_put(skb, IFLA_BR_GROUP_ADDR, ETH_ALEN, br->group_addr)) return -EMSGSIZE; #ifdef CONFIG_BRIDGE_VLAN_FILTERING -- cgit v0.10.2 From 150217c688217e549ef8a36ea4f6718977373765 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 4 Oct 2015 14:23:36 +0200 Subject: bridge: netlink: add fdb flush Simple attribute that flushes the bridge's fdb. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index eaeaac1..9ca9bf8 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -244,6 +244,7 @@ enum { IFLA_BR_TOPOLOGY_CHANGE_TIMER, IFLA_BR_GC_TIMER, IFLA_BR_GROUP_ADDR, + IFLA_BR_FDB_FLUSH, __IFLA_BR_MAX, }; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index a05a430..5853c57 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -859,6 +859,9 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[], br_recalculate_fwd_mask(br); } + if (data[IFLA_BR_FDB_FLUSH]) + br_fdb_flush(br); + return 0; } -- cgit v0.10.2 From a9a6bc70f5f70b3835b081e401b469b88c7c8a3a Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 4 Oct 2015 14:23:37 +0200 Subject: bridge: netlink: add support for multicast_router Add IFLA_BR_MCAST_ROUTER to allow setting and retrieving br->multicast_router when igmp snooping is enabled. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 9ca9bf8..5d2c929 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -245,6 +245,7 @@ enum { IFLA_BR_GC_TIMER, IFLA_BR_GROUP_ADDR, IFLA_BR_FDB_FLUSH, + IFLA_BR_MCAST_ROUTER, __IFLA_BR_MAX, }; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 5853c57..f4df609 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -767,6 +767,7 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = { [IFLA_BR_GROUP_FWD_MASK] = { .type = NLA_U16 }, [IFLA_BR_GROUP_ADDR] = { .type = NLA_BINARY, .len = ETH_ALEN }, + [IFLA_BR_MCAST_ROUTER] = { .type = NLA_U8 }, }; static int br_changelink(struct net_device *brdev, struct nlattr *tb[], @@ -862,6 +863,16 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[], if (data[IFLA_BR_FDB_FLUSH]) br_fdb_flush(br); +#ifdef CONFIG_BRIDGE_IGMP_SNOOPING + if (data[IFLA_BR_MCAST_ROUTER]) { + u8 multicast_router = nla_get_u8(data[IFLA_BR_MCAST_ROUTER]); + + err = br_multicast_set_router(br, multicast_router); + if (err) + return err; + } +#endif + return 0; } @@ -889,6 +900,9 @@ static size_t br_get_size(const struct net_device *brdev) nla_total_size(sizeof(u64)) + /* IFLA_BR_TOPOLOGY_CHANGE_TIMER */ nla_total_size(sizeof(u64)) + /* IFLA_BR_GC_TIMER */ nla_total_size(ETH_ALEN) + /* IFLA_BR_GROUP_ADDR */ +#ifdef CONFIG_BRIDGE_IGMP_SNOOPING + nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_ROUTER */ +#endif 0; } @@ -945,6 +959,11 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) return -EMSGSIZE; #endif +#ifdef CONFIG_BRIDGE_IGMP_SNOOPING + if (nla_put_u8(skb, IFLA_BR_MCAST_ROUTER, br->multicast_router)) + return -EMSGSIZE; +#endif + return 0; } -- cgit v0.10.2 From 89126327f921bd278c72284d38428443bbef344f Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 4 Oct 2015 14:23:38 +0200 Subject: bridge: netlink: add support for multicast_snooping Add IFLA_BR_MCAST_SNOOPING to allow enabling/disabling multicast snooping via netlink. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 5d2c929..22cb395 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -246,6 +246,7 @@ enum { IFLA_BR_GROUP_ADDR, IFLA_BR_FDB_FLUSH, IFLA_BR_MCAST_ROUTER, + IFLA_BR_MCAST_SNOOPING, __IFLA_BR_MAX, }; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index f4df609..25e1c66 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -768,6 +768,7 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = { [IFLA_BR_GROUP_ADDR] = { .type = NLA_BINARY, .len = ETH_ALEN }, [IFLA_BR_MCAST_ROUTER] = { .type = NLA_U8 }, + [IFLA_BR_MCAST_SNOOPING] = { .type = NLA_U8 }, }; static int br_changelink(struct net_device *brdev, struct nlattr *tb[], @@ -871,6 +872,14 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[], if (err) return err; } + + if (data[IFLA_BR_MCAST_SNOOPING]) { + u8 mcast_snooping = nla_get_u8(data[IFLA_BR_MCAST_SNOOPING]); + + err = br_multicast_toggle(br, mcast_snooping); + if (err) + return err; + } #endif return 0; @@ -902,6 +911,7 @@ static size_t br_get_size(const struct net_device *brdev) nla_total_size(ETH_ALEN) + /* IFLA_BR_GROUP_ADDR */ #ifdef CONFIG_BRIDGE_IGMP_SNOOPING nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_ROUTER */ + nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_SNOOPING */ #endif 0; } @@ -960,7 +970,8 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) #endif #ifdef CONFIG_BRIDGE_IGMP_SNOOPING - if (nla_put_u8(skb, IFLA_BR_MCAST_ROUTER, br->multicast_router)) + if (nla_put_u8(skb, IFLA_BR_MCAST_ROUTER, br->multicast_router) || + nla_put_u8(skb, IFLA_BR_MCAST_SNOOPING, !br->multicast_disabled)) return -EMSGSIZE; #endif -- cgit v0.10.2 From 295141d9049bdf4fa316b325d2e2501b210dbe06 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 4 Oct 2015 14:23:39 +0200 Subject: bridge: netlink: add support for multicast_query_use_ifaddr Add IFLA_BR_MCAST_QUERY_USE_IFADDR to allow setting/getting br->multicast_query_use_ifaddr via netlink. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 22cb395..7090b39 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -247,6 +247,7 @@ enum { IFLA_BR_FDB_FLUSH, IFLA_BR_MCAST_ROUTER, IFLA_BR_MCAST_SNOOPING, + IFLA_BR_MCAST_QUERY_USE_IFADDR, __IFLA_BR_MAX, }; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 25e1c66..12ef844 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -769,6 +769,7 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = { .len = ETH_ALEN }, [IFLA_BR_MCAST_ROUTER] = { .type = NLA_U8 }, [IFLA_BR_MCAST_SNOOPING] = { .type = NLA_U8 }, + [IFLA_BR_MCAST_QUERY_USE_IFADDR] = { .type = NLA_U8 }, }; static int br_changelink(struct net_device *brdev, struct nlattr *tb[], @@ -880,6 +881,13 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[], if (err) return err; } + + if (data[IFLA_BR_MCAST_QUERY_USE_IFADDR]) { + u8 val; + + val = nla_get_u8(data[IFLA_BR_MCAST_QUERY_USE_IFADDR]); + br->multicast_query_use_ifaddr = !!val; + } #endif return 0; @@ -912,6 +920,7 @@ static size_t br_get_size(const struct net_device *brdev) #ifdef CONFIG_BRIDGE_IGMP_SNOOPING nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_ROUTER */ nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_SNOOPING */ + nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_QUERY_USE_IFADDR */ #endif 0; } @@ -971,7 +980,9 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) #ifdef CONFIG_BRIDGE_IGMP_SNOOPING if (nla_put_u8(skb, IFLA_BR_MCAST_ROUTER, br->multicast_router) || - nla_put_u8(skb, IFLA_BR_MCAST_SNOOPING, !br->multicast_disabled)) + nla_put_u8(skb, IFLA_BR_MCAST_SNOOPING, !br->multicast_disabled) || + nla_put_u8(skb, IFLA_BR_MCAST_QUERY_USE_IFADDR, + br->multicast_query_use_ifaddr)) return -EMSGSIZE; #endif -- cgit v0.10.2 From ba062d7cc6a09a8194eba975d5ee635378a55bfc Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 4 Oct 2015 14:23:40 +0200 Subject: bridge: netlink: add support for multicast_querier Add IFLA_BR_MCAST_QUERIER to allow setting/getting br->multicast_querier via netlink. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 7090b39..fe5fac5 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -248,6 +248,7 @@ enum { IFLA_BR_MCAST_ROUTER, IFLA_BR_MCAST_SNOOPING, IFLA_BR_MCAST_QUERY_USE_IFADDR, + IFLA_BR_MCAST_QUERIER, __IFLA_BR_MAX, }; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 12ef844..e21296d 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -770,6 +770,7 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = { [IFLA_BR_MCAST_ROUTER] = { .type = NLA_U8 }, [IFLA_BR_MCAST_SNOOPING] = { .type = NLA_U8 }, [IFLA_BR_MCAST_QUERY_USE_IFADDR] = { .type = NLA_U8 }, + [IFLA_BR_MCAST_QUERIER] = { .type = NLA_U8 }, }; static int br_changelink(struct net_device *brdev, struct nlattr *tb[], @@ -888,6 +889,14 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[], val = nla_get_u8(data[IFLA_BR_MCAST_QUERY_USE_IFADDR]); br->multicast_query_use_ifaddr = !!val; } + + if (data[IFLA_BR_MCAST_QUERIER]) { + u8 mcast_querier = nla_get_u8(data[IFLA_BR_MCAST_QUERIER]); + + err = br_multicast_set_querier(br, mcast_querier); + if (err) + return err; + } #endif return 0; @@ -921,6 +930,7 @@ static size_t br_get_size(const struct net_device *brdev) nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_ROUTER */ nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_SNOOPING */ nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_QUERY_USE_IFADDR */ + nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_QUERIER */ #endif 0; } @@ -982,7 +992,8 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) if (nla_put_u8(skb, IFLA_BR_MCAST_ROUTER, br->multicast_router) || nla_put_u8(skb, IFLA_BR_MCAST_SNOOPING, !br->multicast_disabled) || nla_put_u8(skb, IFLA_BR_MCAST_QUERY_USE_IFADDR, - br->multicast_query_use_ifaddr)) + br->multicast_query_use_ifaddr) || + nla_put_u8(skb, IFLA_BR_MCAST_QUERIER, br->multicast_querier)) return -EMSGSIZE; #endif -- cgit v0.10.2 From 431db3c050af0be72b3b01fa7484982f35cb268f Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 4 Oct 2015 14:23:41 +0200 Subject: bridge: netlink: add support for igmp's hash_elasticity Add IFLA_BR_MCAST_HASH_ELASTICITY to allow setting/getting br->hash_elasticity via netlink. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index fe5fac5..ca7ca76 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -249,6 +249,7 @@ enum { IFLA_BR_MCAST_SNOOPING, IFLA_BR_MCAST_QUERY_USE_IFADDR, IFLA_BR_MCAST_QUERIER, + IFLA_BR_MCAST_HASH_ELASTICITY, __IFLA_BR_MAX, }; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index e21296d..b210a63 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -771,6 +771,7 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = { [IFLA_BR_MCAST_SNOOPING] = { .type = NLA_U8 }, [IFLA_BR_MCAST_QUERY_USE_IFADDR] = { .type = NLA_U8 }, [IFLA_BR_MCAST_QUERIER] = { .type = NLA_U8 }, + [IFLA_BR_MCAST_HASH_ELASTICITY] = { .type = NLA_U32 }, }; static int br_changelink(struct net_device *brdev, struct nlattr *tb[], @@ -897,6 +898,12 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[], if (err) return err; } + + if (data[IFLA_BR_MCAST_HASH_ELASTICITY]) { + u32 val = nla_get_u32(data[IFLA_BR_MCAST_HASH_ELASTICITY]); + + br->hash_elasticity = val; + } #endif return 0; @@ -931,6 +938,7 @@ static size_t br_get_size(const struct net_device *brdev) nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_SNOOPING */ nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_QUERY_USE_IFADDR */ nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_QUERIER */ + nla_total_size(sizeof(u32)) + /* IFLA_BR_MCAST_HASH_ELASTICITY */ #endif 0; } @@ -993,7 +1001,9 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) nla_put_u8(skb, IFLA_BR_MCAST_SNOOPING, !br->multicast_disabled) || nla_put_u8(skb, IFLA_BR_MCAST_QUERY_USE_IFADDR, br->multicast_query_use_ifaddr) || - nla_put_u8(skb, IFLA_BR_MCAST_QUERIER, br->multicast_querier)) + nla_put_u8(skb, IFLA_BR_MCAST_QUERIER, br->multicast_querier) || + nla_put_u32(skb, IFLA_BR_MCAST_HASH_ELASTICITY, + br->hash_elasticity)) return -EMSGSIZE; #endif -- cgit v0.10.2 From 858079fdae16421d4908722140346cfdddedf343 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 4 Oct 2015 14:23:42 +0200 Subject: bridge: netlink: add support for igmp's hash_max Add IFLA_BR_MCAST_HASH_MAX to allow setting/getting br->hash_max via netlink. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index ca7ca76..10a44a73 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -250,6 +250,7 @@ enum { IFLA_BR_MCAST_QUERY_USE_IFADDR, IFLA_BR_MCAST_QUERIER, IFLA_BR_MCAST_HASH_ELASTICITY, + IFLA_BR_MCAST_HASH_MAX, __IFLA_BR_MAX, }; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index b210a63..d6b61b0 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -772,6 +772,7 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = { [IFLA_BR_MCAST_QUERY_USE_IFADDR] = { .type = NLA_U8 }, [IFLA_BR_MCAST_QUERIER] = { .type = NLA_U8 }, [IFLA_BR_MCAST_HASH_ELASTICITY] = { .type = NLA_U32 }, + [IFLA_BR_MCAST_HASH_MAX] = { .type = NLA_U32 }, }; static int br_changelink(struct net_device *brdev, struct nlattr *tb[], @@ -904,6 +905,14 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[], br->hash_elasticity = val; } + + if (data[IFLA_BR_MCAST_HASH_MAX]) { + u32 hash_max = nla_get_u32(data[IFLA_BR_MCAST_HASH_MAX]); + + err = br_multicast_set_hash_max(br, hash_max); + if (err) + return err; + } #endif return 0; @@ -939,6 +948,7 @@ static size_t br_get_size(const struct net_device *brdev) nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_QUERY_USE_IFADDR */ nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_QUERIER */ nla_total_size(sizeof(u32)) + /* IFLA_BR_MCAST_HASH_ELASTICITY */ + nla_total_size(sizeof(u32)) + /* IFLA_BR_MCAST_HASH_MAX */ #endif 0; } @@ -1003,7 +1013,8 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) br->multicast_query_use_ifaddr) || nla_put_u8(skb, IFLA_BR_MCAST_QUERIER, br->multicast_querier) || nla_put_u32(skb, IFLA_BR_MCAST_HASH_ELASTICITY, - br->hash_elasticity)) + br->hash_elasticity) || + nla_put_u32(skb, IFLA_BR_MCAST_HASH_MAX, br->hash_max)) return -EMSGSIZE; #endif -- cgit v0.10.2 From 79b859f573d6afa64e328cc7f50ad7a209e0c92d Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 4 Oct 2015 14:23:43 +0200 Subject: bridge: netlink: add support for multicast_last_member_count Add IFLA_BR_MCAST_LAST_MEMBER_CNT to allow setting/getting br->multicast_last_member_count via netlink. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 10a44a73..5409a46 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -251,6 +251,7 @@ enum { IFLA_BR_MCAST_QUERIER, IFLA_BR_MCAST_HASH_ELASTICITY, IFLA_BR_MCAST_HASH_MAX, + IFLA_BR_MCAST_LAST_MEMBER_CNT, __IFLA_BR_MAX, }; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index d6b61b0..cf6ccae 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -773,6 +773,7 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = { [IFLA_BR_MCAST_QUERIER] = { .type = NLA_U8 }, [IFLA_BR_MCAST_HASH_ELASTICITY] = { .type = NLA_U32 }, [IFLA_BR_MCAST_HASH_MAX] = { .type = NLA_U32 }, + [IFLA_BR_MCAST_LAST_MEMBER_CNT] = { .type = NLA_U32 }, }; static int br_changelink(struct net_device *brdev, struct nlattr *tb[], @@ -913,6 +914,12 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[], if (err) return err; } + + if (data[IFLA_BR_MCAST_LAST_MEMBER_CNT]) { + u32 val = nla_get_u32(data[IFLA_BR_MCAST_LAST_MEMBER_CNT]); + + br->multicast_last_member_count = val; + } #endif return 0; @@ -949,6 +956,7 @@ static size_t br_get_size(const struct net_device *brdev) nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_QUERIER */ nla_total_size(sizeof(u32)) + /* IFLA_BR_MCAST_HASH_ELASTICITY */ nla_total_size(sizeof(u32)) + /* IFLA_BR_MCAST_HASH_MAX */ + nla_total_size(sizeof(u32)) + /* IFLA_BR_MCAST_LAST_MEMBER_CNT */ #endif 0; } @@ -1014,7 +1022,9 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) nla_put_u8(skb, IFLA_BR_MCAST_QUERIER, br->multicast_querier) || nla_put_u32(skb, IFLA_BR_MCAST_HASH_ELASTICITY, br->hash_elasticity) || - nla_put_u32(skb, IFLA_BR_MCAST_HASH_MAX, br->hash_max)) + nla_put_u32(skb, IFLA_BR_MCAST_HASH_MAX, br->hash_max) || + nla_put_u32(skb, IFLA_BR_MCAST_LAST_MEMBER_CNT, + br->multicast_last_member_count)) return -EMSGSIZE; #endif -- cgit v0.10.2 From b89e6babad4b7ca7298ad863c6c83dc76b0abdef Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 4 Oct 2015 14:23:44 +0200 Subject: bridge: netlink: add support for multicast_startup_query_count Add IFLA_BR_MCAST_STARTUP_QUERY_CNT to allow setting/getting br->multicast_startup_query_count via netlink. Also align the ifla comments. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 5409a46..fd841b5 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -252,6 +252,7 @@ enum { IFLA_BR_MCAST_HASH_ELASTICITY, IFLA_BR_MCAST_HASH_MAX, IFLA_BR_MCAST_LAST_MEMBER_CNT, + IFLA_BR_MCAST_STARTUP_QUERY_CNT, __IFLA_BR_MAX, }; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index cf6ccae..6744e30 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -774,6 +774,7 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = { [IFLA_BR_MCAST_HASH_ELASTICITY] = { .type = NLA_U32 }, [IFLA_BR_MCAST_HASH_MAX] = { .type = NLA_U32 }, [IFLA_BR_MCAST_LAST_MEMBER_CNT] = { .type = NLA_U32 }, + [IFLA_BR_MCAST_STARTUP_QUERY_CNT] = { .type = NLA_U32 }, }; static int br_changelink(struct net_device *brdev, struct nlattr *tb[], @@ -920,6 +921,12 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[], br->multicast_last_member_count = val; } + + if (data[IFLA_BR_MCAST_STARTUP_QUERY_CNT]) { + u32 val = nla_get_u32(data[IFLA_BR_MCAST_STARTUP_QUERY_CNT]); + + br->multicast_startup_query_count = val; + } #endif return 0; @@ -942,8 +949,8 @@ static size_t br_get_size(const struct net_device *brdev) nla_total_size(sizeof(struct ifla_bridge_id)) + /* IFLA_BR_BRIDGE_ID */ nla_total_size(sizeof(u16)) + /* IFLA_BR_ROOT_PORT */ nla_total_size(sizeof(u32)) + /* IFLA_BR_ROOT_PATH_COST */ - nla_total_size(sizeof(u8)) + /* IFLA_BR_TOPOLOGY_CHANGE */ - nla_total_size(sizeof(u8)) + /* IFLA_BR_TOPOLOGY_CHANGE_DETECTED */ + nla_total_size(sizeof(u8)) + /* IFLA_BR_TOPOLOGY_CHANGE */ + nla_total_size(sizeof(u8)) + /* IFLA_BR_TOPOLOGY_CHANGE_DETECTED */ nla_total_size(sizeof(u64)) + /* IFLA_BR_HELLO_TIMER */ nla_total_size(sizeof(u64)) + /* IFLA_BR_TCN_TIMER */ nla_total_size(sizeof(u64)) + /* IFLA_BR_TOPOLOGY_CHANGE_TIMER */ @@ -954,9 +961,10 @@ static size_t br_get_size(const struct net_device *brdev) nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_SNOOPING */ nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_QUERY_USE_IFADDR */ nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_QUERIER */ - nla_total_size(sizeof(u32)) + /* IFLA_BR_MCAST_HASH_ELASTICITY */ - nla_total_size(sizeof(u32)) + /* IFLA_BR_MCAST_HASH_MAX */ - nla_total_size(sizeof(u32)) + /* IFLA_BR_MCAST_LAST_MEMBER_CNT */ + nla_total_size(sizeof(u32)) + /* IFLA_BR_MCAST_HASH_ELASTICITY */ + nla_total_size(sizeof(u32)) + /* IFLA_BR_MCAST_HASH_MAX */ + nla_total_size(sizeof(u32)) + /* IFLA_BR_MCAST_LAST_MEMBER_CNT */ + nla_total_size(sizeof(u32)) + /* IFLA_BR_MCAST_STARTUP_QUERY_CNT */ #endif 0; } @@ -1024,7 +1032,9 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) br->hash_elasticity) || nla_put_u32(skb, IFLA_BR_MCAST_HASH_MAX, br->hash_max) || nla_put_u32(skb, IFLA_BR_MCAST_LAST_MEMBER_CNT, - br->multicast_last_member_count)) + br->multicast_last_member_count) || + nla_put_u32(skb, IFLA_BR_MCAST_STARTUP_QUERY_CNT, + br->multicast_startup_query_count)) return -EMSGSIZE; #endif -- cgit v0.10.2 From 7e4df51eb35deedd3ba8d4db92a6c36fb7eff90a Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 4 Oct 2015 14:23:45 +0200 Subject: bridge: netlink: add support for igmp's intervals Add support to set/get all of the igmp's configurable intervals via netlink. These currently are: IFLA_BR_MCAST_LAST_MEMBER_INTVL IFLA_BR_MCAST_MEMBERSHIP_INTVL IFLA_BR_MCAST_QUERIER_INTVL IFLA_BR_MCAST_QUERY_INTVL IFLA_BR_MCAST_QUERY_RESPONSE_INTVL IFLA_BR_MCAST_STARTUP_QUERY_INTVL Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index fd841b5..b8c88aa 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -253,6 +253,12 @@ enum { IFLA_BR_MCAST_HASH_MAX, IFLA_BR_MCAST_LAST_MEMBER_CNT, IFLA_BR_MCAST_STARTUP_QUERY_CNT, + IFLA_BR_MCAST_LAST_MEMBER_INTVL, + IFLA_BR_MCAST_MEMBERSHIP_INTVL, + IFLA_BR_MCAST_QUERIER_INTVL, + IFLA_BR_MCAST_QUERY_INTVL, + IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, + IFLA_BR_MCAST_STARTUP_QUERY_INTVL, __IFLA_BR_MAX, }; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 6744e30..30def4f 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -775,6 +775,12 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = { [IFLA_BR_MCAST_HASH_MAX] = { .type = NLA_U32 }, [IFLA_BR_MCAST_LAST_MEMBER_CNT] = { .type = NLA_U32 }, [IFLA_BR_MCAST_STARTUP_QUERY_CNT] = { .type = NLA_U32 }, + [IFLA_BR_MCAST_LAST_MEMBER_INTVL] = { .type = NLA_U64 }, + [IFLA_BR_MCAST_MEMBERSHIP_INTVL] = { .type = NLA_U64 }, + [IFLA_BR_MCAST_QUERIER_INTVL] = { .type = NLA_U64 }, + [IFLA_BR_MCAST_QUERY_INTVL] = { .type = NLA_U64 }, + [IFLA_BR_MCAST_QUERY_RESPONSE_INTVL] = { .type = NLA_U64 }, + [IFLA_BR_MCAST_STARTUP_QUERY_INTVL] = { .type = NLA_U64 }, }; static int br_changelink(struct net_device *brdev, struct nlattr *tb[], @@ -927,6 +933,42 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[], br->multicast_startup_query_count = val; } + + if (data[IFLA_BR_MCAST_LAST_MEMBER_INTVL]) { + u64 val = nla_get_u64(data[IFLA_BR_MCAST_LAST_MEMBER_INTVL]); + + br->multicast_last_member_interval = clock_t_to_jiffies(val); + } + + if (data[IFLA_BR_MCAST_MEMBERSHIP_INTVL]) { + u64 val = nla_get_u64(data[IFLA_BR_MCAST_MEMBERSHIP_INTVL]); + + br->multicast_membership_interval = clock_t_to_jiffies(val); + } + + if (data[IFLA_BR_MCAST_QUERIER_INTVL]) { + u64 val = nla_get_u64(data[IFLA_BR_MCAST_QUERIER_INTVL]); + + br->multicast_querier_interval = clock_t_to_jiffies(val); + } + + if (data[IFLA_BR_MCAST_QUERY_INTVL]) { + u64 val = nla_get_u64(data[IFLA_BR_MCAST_QUERY_INTVL]); + + br->multicast_query_interval = clock_t_to_jiffies(val); + } + + if (data[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL]) { + u64 val = nla_get_u64(data[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL]); + + br->multicast_query_response_interval = clock_t_to_jiffies(val); + } + + if (data[IFLA_BR_MCAST_STARTUP_QUERY_INTVL]) { + u64 val = nla_get_u64(data[IFLA_BR_MCAST_STARTUP_QUERY_INTVL]); + + br->multicast_startup_query_interval = clock_t_to_jiffies(val); + } #endif return 0; @@ -965,6 +1007,12 @@ static size_t br_get_size(const struct net_device *brdev) nla_total_size(sizeof(u32)) + /* IFLA_BR_MCAST_HASH_MAX */ nla_total_size(sizeof(u32)) + /* IFLA_BR_MCAST_LAST_MEMBER_CNT */ nla_total_size(sizeof(u32)) + /* IFLA_BR_MCAST_STARTUP_QUERY_CNT */ + nla_total_size(sizeof(u64)) + /* IFLA_BR_MCAST_LAST_MEMBER_INTVL */ + nla_total_size(sizeof(u64)) + /* IFLA_BR_MCAST_MEMBERSHIP_INTVL */ + nla_total_size(sizeof(u64)) + /* IFLA_BR_MCAST_QUERIER_INTVL */ + nla_total_size(sizeof(u64)) + /* IFLA_BR_MCAST_QUERY_INTVL */ + nla_total_size(sizeof(u64)) + /* IFLA_BR_MCAST_QUERY_RESPONSE_INTVL */ + nla_total_size(sizeof(u64)) + /* IFLA_BR_MCAST_STARTUP_QUERY_INTVL */ #endif 0; } @@ -972,7 +1020,7 @@ static size_t br_get_size(const struct net_device *brdev) static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) { struct net_bridge *br = netdev_priv(brdev); - u64 hello_timer, tcn_timer, topology_change_timer, gc_timer; + u64 hello_timer, tcn_timer, topology_change_timer, gc_timer, clockval; u32 forward_delay = jiffies_to_clock_t(br->forward_delay); u32 hello_time = jiffies_to_clock_t(br->hello_time); u32 age_time = jiffies_to_clock_t(br->max_age); @@ -993,6 +1041,7 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) tcn_timer = br_timer_value(&br->tcn_timer); topology_change_timer = br_timer_value(&br->topology_change_timer); gc_timer = br_timer_value(&br->gc_timer); + clockval = 0; if (nla_put_u32(skb, IFLA_BR_FORWARD_DELAY, forward_delay) || nla_put_u32(skb, IFLA_BR_HELLO_TIME, hello_time) || @@ -1036,6 +1085,25 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) nla_put_u32(skb, IFLA_BR_MCAST_STARTUP_QUERY_CNT, br->multicast_startup_query_count)) return -EMSGSIZE; + + clockval = jiffies_to_clock_t(br->multicast_last_member_interval); + if (nla_put_u64(skb, IFLA_BR_MCAST_LAST_MEMBER_INTVL, clockval)) + return -EMSGSIZE; + clockval = jiffies_to_clock_t(br->multicast_membership_interval); + if (nla_put_u64(skb, IFLA_BR_MCAST_MEMBERSHIP_INTVL, clockval)) + return -EMSGSIZE; + clockval = jiffies_to_clock_t(br->multicast_querier_interval); + if (nla_put_u64(skb, IFLA_BR_MCAST_QUERIER_INTVL, clockval)) + return -EMSGSIZE; + clockval = jiffies_to_clock_t(br->multicast_query_interval); + if (nla_put_u64(skb, IFLA_BR_MCAST_QUERY_INTVL, clockval)) + return -EMSGSIZE; + clockval = jiffies_to_clock_t(br->multicast_query_response_interval); + if (nla_put_u64(skb, IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, clockval)) + return -EMSGSIZE; + clockval = jiffies_to_clock_t(br->multicast_startup_query_interval); + if (nla_put_u64(skb, IFLA_BR_MCAST_STARTUP_QUERY_INTVL, clockval)) + return -EMSGSIZE; #endif return 0; -- cgit v0.10.2 From 93870cc02a0af4392401713d14235accafc752bc Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 4 Oct 2015 14:23:46 +0200 Subject: bridge: netlink: add support for netfilter tables config Add support to allow getting/setting netfilter tables settings. Currently these are IFLA_BR_NF_CALL_IPTABLES, IFLA_BR_NF_CALL_IP6TABLES and IFLA_BR_NF_CALL_ARPTABLES. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index b8c88aa..0200bed 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -259,6 +259,9 @@ enum { IFLA_BR_MCAST_QUERY_INTVL, IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, IFLA_BR_MCAST_STARTUP_QUERY_INTVL, + IFLA_BR_NF_CALL_IPTABLES, + IFLA_BR_NF_CALL_IP6TABLES, + IFLA_BR_NF_CALL_ARPTABLES, __IFLA_BR_MAX, }; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 30def4f..fd37caf 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -781,6 +781,9 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = { [IFLA_BR_MCAST_QUERY_INTVL] = { .type = NLA_U64 }, [IFLA_BR_MCAST_QUERY_RESPONSE_INTVL] = { .type = NLA_U64 }, [IFLA_BR_MCAST_STARTUP_QUERY_INTVL] = { .type = NLA_U64 }, + [IFLA_BR_NF_CALL_IPTABLES] = { .type = NLA_U8 }, + [IFLA_BR_NF_CALL_IP6TABLES] = { .type = NLA_U8 }, + [IFLA_BR_NF_CALL_ARPTABLES] = { .type = NLA_U8 }, }; static int br_changelink(struct net_device *brdev, struct nlattr *tb[], @@ -970,6 +973,25 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[], br->multicast_startup_query_interval = clock_t_to_jiffies(val); } #endif +#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) + if (data[IFLA_BR_NF_CALL_IPTABLES]) { + u8 val = nla_get_u8(data[IFLA_BR_NF_CALL_IPTABLES]); + + br->nf_call_iptables = val ? true : false; + } + + if (data[IFLA_BR_NF_CALL_IP6TABLES]) { + u8 val = nla_get_u8(data[IFLA_BR_NF_CALL_IP6TABLES]); + + br->nf_call_ip6tables = val ? true : false; + } + + if (data[IFLA_BR_NF_CALL_ARPTABLES]) { + u8 val = nla_get_u8(data[IFLA_BR_NF_CALL_ARPTABLES]); + + br->nf_call_arptables = val ? true : false; + } +#endif return 0; } @@ -1014,6 +1036,11 @@ static size_t br_get_size(const struct net_device *brdev) nla_total_size(sizeof(u64)) + /* IFLA_BR_MCAST_QUERY_RESPONSE_INTVL */ nla_total_size(sizeof(u64)) + /* IFLA_BR_MCAST_STARTUP_QUERY_INTVL */ #endif +#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) + nla_total_size(sizeof(u8)) + /* IFLA_BR_NF_CALL_IPTABLES */ + nla_total_size(sizeof(u8)) + /* IFLA_BR_NF_CALL_IP6TABLES */ + nla_total_size(sizeof(u8)) + /* IFLA_BR_NF_CALL_ARPTABLES */ +#endif 0; } @@ -1070,7 +1097,6 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) if (nla_put_be16(skb, IFLA_BR_VLAN_PROTOCOL, br->vlan_proto)) return -EMSGSIZE; #endif - #ifdef CONFIG_BRIDGE_IGMP_SNOOPING if (nla_put_u8(skb, IFLA_BR_MCAST_ROUTER, br->multicast_router) || nla_put_u8(skb, IFLA_BR_MCAST_SNOOPING, !br->multicast_disabled) || @@ -1105,6 +1131,15 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) if (nla_put_u64(skb, IFLA_BR_MCAST_STARTUP_QUERY_INTVL, clockval)) return -EMSGSIZE; #endif +#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) + if (nla_put_u8(skb, IFLA_BR_NF_CALL_IPTABLES, + br->nf_call_iptables ? 1 : 0) || + nla_put_u8(skb, IFLA_BR_NF_CALL_IP6TABLES, + br->nf_call_ip6tables ? 1 : 0) || + nla_put_u8(skb, IFLA_BR_NF_CALL_ARPTABLES, + br->nf_call_arptables ? 1 : 0)) + return -EMSGSIZE; +#endif return 0; } -- cgit v0.10.2 From 0f963b7592ef9e054974b6672b86ec1edd84b4bc Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 4 Oct 2015 14:23:47 +0200 Subject: bridge: netlink: add support for default_pvid Add IFLA_BR_VLAN_DEFAULT_PVID to allow setting/getting bridge's default_pvid via netlink. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 0200bed..c5b15bf 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -262,6 +262,7 @@ enum { IFLA_BR_NF_CALL_IPTABLES, IFLA_BR_NF_CALL_IP6TABLES, IFLA_BR_NF_CALL_ARPTABLES, + IFLA_BR_VLAN_DEFAULT_PVID, __IFLA_BR_MAX, }; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index fd37caf..70efe2e 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -784,6 +784,7 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = { [IFLA_BR_NF_CALL_IPTABLES] = { .type = NLA_U8 }, [IFLA_BR_NF_CALL_IP6TABLES] = { .type = NLA_U8 }, [IFLA_BR_NF_CALL_ARPTABLES] = { .type = NLA_U8 }, + [IFLA_BR_VLAN_DEFAULT_PVID] = { .type = NLA_U16 }, }; static int br_changelink(struct net_device *brdev, struct nlattr *tb[], @@ -847,6 +848,14 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[], if (err) return err; } + + if (data[IFLA_BR_VLAN_DEFAULT_PVID]) { + __u16 defpvid = nla_get_u16(data[IFLA_BR_VLAN_DEFAULT_PVID]); + + err = __br_vlan_set_default_pvid(br, defpvid); + if (err) + return err; + } #endif if (data[IFLA_BR_GROUP_FWD_MASK]) { @@ -1007,6 +1016,7 @@ static size_t br_get_size(const struct net_device *brdev) nla_total_size(sizeof(u8)) + /* IFLA_BR_VLAN_FILTERING */ #ifdef CONFIG_BRIDGE_VLAN_FILTERING nla_total_size(sizeof(__be16)) + /* IFLA_BR_VLAN_PROTOCOL */ + nla_total_size(sizeof(u16)) + /* IFLA_BR_VLAN_DEFAULT_PVID */ #endif nla_total_size(sizeof(u16)) + /* IFLA_BR_GROUP_FWD_MASK */ nla_total_size(sizeof(struct ifla_bridge_id)) + /* IFLA_BR_ROOT_ID */ @@ -1094,7 +1104,8 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) return -EMSGSIZE; #ifdef CONFIG_BRIDGE_VLAN_FILTERING - if (nla_put_be16(skb, IFLA_BR_VLAN_PROTOCOL, br->vlan_proto)) + if (nla_put_be16(skb, IFLA_BR_VLAN_PROTOCOL, br->vlan_proto) || + nla_put_u16(skb, IFLA_BR_VLAN_DEFAULT_PVID, br->default_pvid)) return -EMSGSIZE; #endif #ifdef CONFIG_BRIDGE_IGMP_SNOOPING diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 1ff6a0f..09d3ecb 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -690,6 +690,7 @@ int __br_vlan_set_proto(struct net_bridge *br, __be16 proto); int br_vlan_set_proto(struct net_bridge *br, unsigned long val); int br_vlan_init(struct net_bridge *br); int br_vlan_set_default_pvid(struct net_bridge *br, unsigned long val); +int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid); int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags); int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); void nbp_vlan_flush(struct net_bridge_port *port); diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index b879111..eae07ee 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -727,7 +727,7 @@ static void br_vlan_disable_default_pvid(struct net_bridge *br) br->default_pvid = 0; } -static int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid) +int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid) { const struct net_bridge_vlan *pvent; struct net_bridge_port *p; @@ -735,6 +735,11 @@ static int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid) int err = 0; unsigned long *changed; + if (!pvid) { + br_vlan_disable_default_pvid(br); + return 0; + } + changed = kcalloc(BITS_TO_LONGS(BR_MAX_PORTS), sizeof(unsigned long), GFP_KERNEL); if (!changed) @@ -825,12 +830,7 @@ int br_vlan_set_default_pvid(struct net_bridge *br, unsigned long val) err = -EPERM; goto unlock; } - - if (!pvid) - br_vlan_disable_default_pvid(br); - else - err = __br_vlan_set_default_pvid(br, pvid); - + err = __br_vlan_set_default_pvid(br, pvid); unlock: rtnl_unlock(); return err; -- cgit v0.10.2 From b224d3ff717cc3af91a4ec74d863a176d79331af Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 3 Oct 2015 12:01:08 +0200 Subject: Bluetooth: btbcm: Send HCI Reset before sending Apple specific commands The commit 7bee8b08c428 allows the Read Verbose Config Info to fail gracefully and not cause the controller setup to abort. It seems the reason that command failed in the first place was the missing HCI Reset to bring the controller in full Bluetooth mode. Apple Bluetooth controllers start out in HID mode and when in that mode the Read Verbose Config Info command is not allowed. Sending HCI Reset switches the controller into full HCI mode. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c index 02ed816..ad7371d 100644 --- a/drivers/bluetooth/btbcm.c +++ b/drivers/bluetooth/btbcm.c @@ -475,12 +475,18 @@ EXPORT_SYMBOL_GPL(btbcm_setup_patchram); int btbcm_setup_apple(struct hci_dev *hdev) { struct sk_buff *skb; + int err; + + /* Reset */ + err = btbcm_reset(hdev); + if (err) + return err; /* Read Verbose Config Version Info */ skb = btbcm_read_verbose_config(hdev); if (!IS_ERR(skb)) { - BT_INFO("%s: BCM: chip id %u build %4.4u", hdev->name, skb->data[1], - get_unaligned_le16(skb->data + 5)); + BT_INFO("%s: BCM: chip id %u build %4.4u", hdev->name, + skb->data[1], get_unaligned_le16(skb->data + 5)); kfree_skb(skb); } -- cgit v0.10.2 From ed1b28a48b6c4e206bd88f5758393261710566f2 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 4 Oct 2015 23:33:59 +0200 Subject: Bluetooth: Limit userspace exposure of stack internal events The stack internal events that are exposed to userspace should be limited to HCI_DEV_REG, HCI_DEV_UNREG, HCI_DEV_UP and HCI_DEV_DOWN. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 1505563..d9ad684 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -392,14 +392,12 @@ static void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data) void hci_sock_dev_event(struct hci_dev *hdev, int event) { - struct hci_ev_si_device ev; - BT_DBG("hdev %s event %d", hdev->name, event); - /* Send event to monitor */ if (atomic_read(&monitor_promisc)) { struct sk_buff *skb; + /* Send event to monitor */ skb = create_monitor_event(hdev, event); if (skb) { hci_send_to_channel(HCI_CHANNEL_MONITOR, skb, @@ -408,10 +406,14 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event) } } - /* Send event to sockets */ - ev.event = event; - ev.dev_id = hdev->id; - hci_si_event(NULL, HCI_EV_SI_DEVICE, sizeof(ev), &ev); + if (event <= HCI_DEV_DOWN) { + struct hci_ev_si_device ev; + + /* Send event to sockets */ + ev.event = event; + ev.dev_id = hdev->id; + hci_si_event(NULL, HCI_EV_SI_DEVICE, sizeof(ev), &ev); + } if (event == HCI_DEV_UNREG) { struct sock *sk; -- cgit v0.10.2 From 4a3f95b7b62e50a1e42e42ba6571ec9e747f4861 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 4 Oct 2015 23:34:00 +0200 Subject: Bluetooth: Introduce HCI_DEV_OPEN and HCI_DEV_CLOSE events When opening the HCI transport via hdev->open send HCI_DEV_OPEN event and when closing the HCI transport via hdev->close send HCI_DEV_CLOSE. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 7ca6690..e7f938c 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -44,6 +44,8 @@ #define HCI_DEV_DOWN 4 #define HCI_DEV_SUSPEND 5 #define HCI_DEV_RESUME 6 +#define HCI_DEV_OPEN 7 +#define HCI_DEV_CLOSE 8 /* HCI notify events */ #define HCI_NOTIFY_CONN_ADD 1 diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 7935646..5af33c8 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1385,6 +1385,8 @@ static int hci_dev_do_open(struct hci_dev *hdev) goto done; } + hci_notify(hdev, HCI_DEV_OPEN); + atomic_set(&hdev->cmd_cnt, 1); set_bit(HCI_INIT, &hdev->flags); @@ -1466,6 +1468,8 @@ static int hci_dev_do_open(struct hci_dev *hdev) hdev->sent_cmd = NULL; } + hci_notify(hdev, HCI_DEV_CLOSE); + hdev->close(hdev); hdev->flags &= BIT(HCI_RAW); } @@ -1649,6 +1653,8 @@ int hci_dev_do_close(struct hci_dev *hdev) hdev->sent_cmd = NULL; } + hci_notify(hdev, HCI_DEV_CLOSE); + /* After this point our queues are empty * and no tasks are scheduled. */ hdev->close(hdev); -- cgit v0.10.2 From 73d0d3c8671190ea982a8e79a7c79fbfe88f8f47 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 4 Oct 2015 23:34:01 +0200 Subject: Bluetooth: Move HCI_RUNNING check into hci_send_frame In all callbacks for hdev->send the status of HCI_RUNNING is checked. So instead of repeating that code in every driver, move the check into the hci_send_frame function before calling hdev->send. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c index a5c4d05..3ba8170 100644 --- a/drivers/bluetooth/bfusb.c +++ b/drivers/bluetooth/bfusb.c @@ -479,9 +479,6 @@ static int bfusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, bt_cb(skb)->pkt_type, skb->len); - if (!test_bit(HCI_RUNNING, &hdev->flags)) - return -EBUSY; - switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c index 8a31991..59b8924 100644 --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -360,9 +360,6 @@ static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s", hdev->name); - if (!test_bit(HCI_RUNNING, &hdev->flags)) - return -EBUSY; - skb->dev = (void *) hdev; urb = usb_alloc_urb(0, GFP_ATOMIC); diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index 050ad6b..39552a8 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -436,13 +436,6 @@ static int btmrvl_send_frame(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("type=%d, len=%d", skb->pkt_type, skb->len); - if (!test_bit(HCI_RUNNING, &hdev->flags)) { - BT_ERR("Failed testing HCI_RUNING, flags=%lx", hdev->flags); - print_hex_dump_bytes("data: ", DUMP_PREFIX_OFFSET, - skb->data, skb->len); - return -EBUSY; - } - switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c index 83f6437..21f99cc 100644 --- a/drivers/bluetooth/btsdio.c +++ b/drivers/bluetooth/btsdio.c @@ -261,9 +261,6 @@ static int btsdio_send_frame(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s", hdev->name); - if (!test_bit(HCI_RUNNING, &hdev->flags)) - return -EBUSY; - switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index dfaaea2..9cf3796 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -1156,9 +1156,6 @@ static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s", hdev->name); - if (!test_bit(HCI_RUNNING, &hdev->flags)) - return -EBUSY; - switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: urb = alloc_ctrl_urb(hdev, skb); @@ -1843,9 +1840,6 @@ static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s", hdev->name); - if (!test_bit(HCI_RUNNING, &hdev->flags)) - return -EBUSY; - switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: if (test_bit(BTUSB_BOOTLOADER, &data->flags)) { diff --git a/drivers/bluetooth/btwilink.c b/drivers/bluetooth/btwilink.c index 7a722df..4db99a4 100644 --- a/drivers/bluetooth/btwilink.c +++ b/drivers/bluetooth/btwilink.c @@ -256,9 +256,6 @@ static int ti_st_send_frame(struct hci_dev *hdev, struct sk_buff *skb) struct ti_st *hst; long len; - if (!test_bit(HCI_RUNNING, &hdev->flags)) - return -EBUSY; - hst = hci_get_drvdata(hdev); /* Prepend skb with frame type */ diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index ad66c69..443feae 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -254,9 +254,6 @@ static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_uart *hu = hci_get_drvdata(hdev); - if (!test_bit(HCI_RUNNING, &hdev->flags)) - return -EBUSY; - BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len); hu->proto->enqueue(hu, skb); diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index 78653db..15c344c 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -85,9 +85,6 @@ static int vhci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { struct vhci_data *data = hci_get_drvdata(hdev); - if (!test_bit(HCI_RUNNING, &hdev->flags)) - return -EBUSY; - memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); skb_queue_tail(&data->readq, skb); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 5af33c8..b955f71 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3536,6 +3536,11 @@ static void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) /* Get rid of skb owner, prior to sending to the driver. */ skb_orphan(skb); + if (!test_bit(HCI_RUNNING, &hdev->flags)) { + kfree_skb(skb); + return; + } + err = hdev->send(hdev, skb); if (err < 0) { BT_ERR("%s sending frame failed (%d)", hdev->name, err); -- cgit v0.10.2 From e9ca8bf157f2b45f8f670517c96da313083ee9b2 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 4 Oct 2015 23:34:02 +0200 Subject: Bluetooth: Move handling of HCI_RUNNING flag into core Setting and clearing of HCI_RUNNING flag in each and every driver is just duplicating the same code all over the place. So instead of having the driver do it in their hdev->open and hdev->close callbacks, set it globally in the core transport handling. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c index 3ba8170..616ec2a 100644 --- a/drivers/bluetooth/bfusb.c +++ b/drivers/bluetooth/bfusb.c @@ -422,17 +422,12 @@ static int bfusb_open(struct hci_dev *hdev) BT_DBG("hdev %p bfusb %p", hdev, data); - if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) - return 0; - write_lock_irqsave(&data->lock, flags); err = bfusb_rx_submit(data, NULL); if (!err) { for (i = 1; i < BFUSB_MAX_BULK_RX; i++) bfusb_rx_submit(data, NULL); - } else { - clear_bit(HCI_RUNNING, &hdev->flags); } write_unlock_irqrestore(&data->lock, flags); @@ -458,9 +453,6 @@ static int bfusb_close(struct hci_dev *hdev) BT_DBG("hdev %p bfusb %p", hdev, data); - if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) - return 0; - write_lock_irqsave(&data->lock, flags); write_unlock_irqrestore(&data->lock, flags); diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index 25b7166..36fa1c9 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -628,9 +628,6 @@ static int bluecard_hci_open(struct hci_dev *hdev) if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE); - if (test_and_set_bit(HCI_RUNNING, &(hdev->flags))) - return 0; - if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) { unsigned int iobase = info->p_dev->resource[0]->start; @@ -646,9 +643,6 @@ static int bluecard_hci_close(struct hci_dev *hdev) { struct bluecard_info *info = hci_get_drvdata(hdev); - if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags))) - return 0; - bluecard_hci_flush(hdev); if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) { diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c index 59b8924..88e004e 100644 --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -304,9 +304,6 @@ static int bpa10x_open(struct hci_dev *hdev) BT_DBG("%s", hdev->name); - if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) - return 0; - err = bpa10x_submit_intr_urb(hdev); if (err < 0) goto error; @@ -320,8 +317,6 @@ static int bpa10x_open(struct hci_dev *hdev) error: usb_kill_anchored_urbs(&data->rx_anchor); - clear_bit(HCI_RUNNING, &hdev->flags); - return err; } @@ -331,9 +326,6 @@ static int bpa10x_close(struct hci_dev *hdev) BT_DBG("%s", hdev->name); - if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) - return 0; - usb_kill_anchored_urbs(&data->rx_anchor); return 0; diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index b8f4b63..5803aae 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -270,7 +270,6 @@ static void bt3c_receive(struct bt3c_info *info) /* Unknown packet */ BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type); info->hdev->stat.err_rx++; - clear_bit(HCI_RUNNING, &(info->hdev->flags)); kfree_skb(info->rx_skb); info->rx_skb = NULL; @@ -395,17 +394,12 @@ static int bt3c_hci_flush(struct hci_dev *hdev) static int bt3c_hci_open(struct hci_dev *hdev) { - set_bit(HCI_RUNNING, &(hdev->flags)); - return 0; } static int bt3c_hci_close(struct hci_dev *hdev) { - if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags))) - return 0; - bt3c_hci_flush(hdev); return 0; diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index 39552a8..6ba2286 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -470,9 +470,6 @@ static int btmrvl_close(struct hci_dev *hdev) { struct btmrvl_private *priv = hci_get_drvdata(hdev); - if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) - return 0; - skb_queue_purge(&priv->adapter->tx_queue); return 0; @@ -480,8 +477,6 @@ static int btmrvl_close(struct hci_dev *hdev) static int btmrvl_open(struct hci_dev *hdev) { - set_bit(HCI_RUNNING, &hdev->flags); - return 0; } diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c index 21f99cc..7b62442 100644 --- a/drivers/bluetooth/btsdio.c +++ b/drivers/bluetooth/btsdio.c @@ -194,21 +194,15 @@ static int btsdio_open(struct hci_dev *hdev) BT_DBG("%s", hdev->name); - if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) - return 0; - sdio_claim_host(data->func); err = sdio_enable_func(data->func); - if (err < 0) { - clear_bit(HCI_RUNNING, &hdev->flags); + if (err < 0) goto release; - } err = sdio_claim_irq(data->func, btsdio_interrupt); if (err < 0) { sdio_disable_func(data->func); - clear_bit(HCI_RUNNING, &hdev->flags); goto release; } @@ -229,9 +223,6 @@ static int btsdio_close(struct hci_dev *hdev) BT_DBG("%s", hdev->name); - if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) - return 0; - sdio_claim_host(data->func); sdio_writeb(data->func, 0x00, REG_EN_INTRD, NULL); diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index 4c001a8..bb8e402 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -223,7 +223,6 @@ static void btuart_receive(struct btuart_info *info) /* Unknown packet */ BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type); info->hdev->stat.err_rx++; - clear_bit(HCI_RUNNING, &(info->hdev->flags)); kfree_skb(info->rx_skb); info->rx_skb = NULL; @@ -409,17 +408,12 @@ static int btuart_hci_flush(struct hci_dev *hdev) static int btuart_hci_open(struct hci_dev *hdev) { - set_bit(HCI_RUNNING, &(hdev->flags)); - return 0; } static int btuart_hci_close(struct hci_dev *hdev) { - if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags))) - return 0; - btuart_hci_flush(hdev); return 0; diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 9cf3796..247b106 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -940,9 +940,6 @@ static int btusb_open(struct hci_dev *hdev) data->intf->needs_remote_wakeup = 1; - if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) - goto done; - if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags)) goto done; @@ -965,7 +962,6 @@ done: failed: clear_bit(BTUSB_INTR_RUNNING, &data->flags); - clear_bit(HCI_RUNNING, &hdev->flags); usb_autopm_put_interface(data->intf); return err; } @@ -984,9 +980,6 @@ static int btusb_close(struct hci_dev *hdev) BT_DBG("%s", hdev->name); - if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) - return 0; - cancel_work_sync(&data->work); cancel_work_sync(&data->waker); diff --git a/drivers/bluetooth/btwilink.c b/drivers/bluetooth/btwilink.c index 4db99a4..57eb935 100644 --- a/drivers/bluetooth/btwilink.c +++ b/drivers/bluetooth/btwilink.c @@ -155,9 +155,6 @@ static int ti_st_open(struct hci_dev *hdev) BT_DBG("%s %p", hdev->name, hdev); - if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) - return -EBUSY; - /* provide contexts for callbacks from ST */ hst = hci_get_drvdata(hdev); @@ -181,7 +178,6 @@ static int ti_st_open(struct hci_dev *hdev) goto done; if (err != -EINPROGRESS) { - clear_bit(HCI_RUNNING, &hdev->flags); BT_ERR("st_register failed %d", err); return err; } @@ -195,7 +191,6 @@ static int ti_st_open(struct hci_dev *hdev) (&hst->wait_reg_completion, msecs_to_jiffies(BT_REGISTER_TIMEOUT)); if (!timeleft) { - clear_bit(HCI_RUNNING, &hdev->flags); BT_ERR("Timeout(%d sec),didn't get reg " "completion signal from ST", BT_REGISTER_TIMEOUT / 1000); @@ -205,7 +200,6 @@ static int ti_st_open(struct hci_dev *hdev) /* Is ST registration callback * called with ERROR status? */ if (hst->reg_status != 0) { - clear_bit(HCI_RUNNING, &hdev->flags); BT_ERR("ST registration completed with invalid " "status %d", hst->reg_status); return -EAGAIN; @@ -215,7 +209,6 @@ done: hst->st_write = ti_st_proto[i].write; if (!hst->st_write) { BT_ERR("undefined ST write function"); - clear_bit(HCI_RUNNING, &hdev->flags); for (i = 0; i < MAX_BT_CHNL_IDS; i++) { /* Undo registration with ST */ err = st_unregister(&ti_st_proto[i]); @@ -236,9 +229,6 @@ static int ti_st_close(struct hci_dev *hdev) int err, i; struct ti_st *hst = hci_get_drvdata(hdev); - if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) - return 0; - for (i = MAX_BT_CHNL_IDS-1; i >= 0; i--) { err = st_unregister(&ti_st_proto[i]); if (err) diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index 84135c5..5026f66 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -357,8 +357,6 @@ static irqreturn_t dtl1_interrupt(int irq, void *dev_inst) static int dtl1_hci_open(struct hci_dev *hdev) { - set_bit(HCI_RUNNING, &(hdev->flags)); - return 0; } @@ -376,9 +374,6 @@ static int dtl1_hci_flush(struct hci_dev *hdev) static int dtl1_hci_close(struct hci_dev *hdev) { - if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags))) - return 0; - dtl1_hci_flush(hdev); return 0; diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 443feae..01a83a3 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -208,9 +208,6 @@ static int hci_uart_open(struct hci_dev *hdev) BT_DBG("%s %p", hdev->name, hdev); /* Nothing to do for UART driver */ - - set_bit(HCI_RUNNING, &hdev->flags); - return 0; } @@ -241,9 +238,6 @@ static int hci_uart_close(struct hci_dev *hdev) { BT_DBG("hdev %p", hdev); - if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) - return 0; - hci_uart_flush(hdev); hdev->flush = NULL; return 0; diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index 15c344c..ed888e3 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -55,8 +55,6 @@ struct vhci_data { static int vhci_open_dev(struct hci_dev *hdev) { - set_bit(HCI_RUNNING, &hdev->flags); - return 0; } @@ -64,9 +62,6 @@ static int vhci_close_dev(struct hci_dev *hdev) { struct vhci_data *data = hci_get_drvdata(hdev); - if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) - return 0; - skb_queue_purge(&data->readq); return 0; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index b955f71..40a6701 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1385,6 +1385,7 @@ static int hci_dev_do_open(struct hci_dev *hdev) goto done; } + set_bit(HCI_RUNNING, &hdev->flags); hci_notify(hdev, HCI_DEV_OPEN); atomic_set(&hdev->cmd_cnt, 1); @@ -1468,6 +1469,7 @@ static int hci_dev_do_open(struct hci_dev *hdev) hdev->sent_cmd = NULL; } + clear_bit(HCI_RUNNING, &hdev->flags); hci_notify(hdev, HCI_DEV_CLOSE); hdev->close(hdev); @@ -1653,6 +1655,7 @@ int hci_dev_do_close(struct hci_dev *hdev) hdev->sent_cmd = NULL; } + clear_bit(HCI_RUNNING, &hdev->flags); hci_notify(hdev, HCI_DEV_CLOSE); /* After this point our queues are empty -- cgit v0.10.2 From 22db3cbcf9f91eef848db0986869822b4bf27193 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 4 Oct 2015 23:34:03 +0200 Subject: Bluetooth: Send transport open and close monitor events When the core starts or shuts down the actual HCI transport, send a new monitor event that indicates that this is happening. These new events correspond to HCI_DEV_OPEN and HCI_DEV_CLOSE events. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/hci_mon.h b/include/net/bluetooth/hci_mon.h index 77d1e57..37e4283 100644 --- a/include/net/bluetooth/hci_mon.h +++ b/include/net/bluetooth/hci_mon.h @@ -39,6 +39,8 @@ struct hci_mon_hdr { #define HCI_MON_ACL_RX_PKT 5 #define HCI_MON_SCO_TX_PKT 6 #define HCI_MON_SCO_RX_PKT 7 +#define HCI_MON_OPEN_INDEX 8 +#define HCI_MON_CLOSE_INDEX 9 struct hci_mon_new_index { __u8 type; diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index d9ad684..64ebe84 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -329,6 +329,22 @@ static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event) opcode = cpu_to_le16(HCI_MON_DEL_INDEX); break; + case HCI_DEV_OPEN: + skb = bt_skb_alloc(0, GFP_ATOMIC); + if (!skb) + return NULL; + + opcode = cpu_to_le16(HCI_MON_OPEN_INDEX); + break; + + case HCI_DEV_CLOSE: + skb = bt_skb_alloc(0, GFP_ATOMIC); + if (!skb) + return NULL; + + opcode = cpu_to_le16(HCI_MON_CLOSE_INDEX); + break; + default: return NULL; } @@ -358,6 +374,16 @@ static void send_monitor_replay(struct sock *sk) if (sock_queue_rcv_skb(sk, skb)) kfree_skb(skb); + + if (!test_bit(HCI_RUNNING, &hdev->flags)) + continue; + + skb = create_monitor_event(hdev, HCI_DEV_OPEN); + if (!skb) + continue; + + if (sock_queue_rcv_skb(sk, skb)) + kfree_skb(skb); } read_unlock(&hci_dev_list_lock); -- cgit v0.10.2 From 09eef3307e6d55afbdc4cebc8aeacbbb0a7337f6 Mon Sep 17 00:00:00 2001 From: Aviya Erenfeld Date: Tue, 1 Sep 2015 19:34:38 +0300 Subject: iwlwifi: mvm: move DTS command and notification to new group Move the DTS measurement command and notification from short command header to the new PHY command group for firmware supporting the extended command headers. Signed-off-by: Aviya Erenfeld Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index 5e4014a..44ff684 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h @@ -268,6 +268,16 @@ enum { REPLY_MAX = 0xff, }; +enum iwl_phy_ops_subcmd_ids { + CMD_DTS_MEASUREMENT_TRIGGER_WIDE = 0x0, + DTS_MEASUREMENT_NOTIF_WIDE = 0xFF, +}; + +/* command groups */ +enum { + PHY_OPS_GROUP = 0x4, +}; + /** * struct iwl_cmd_response - generic response struct for most commands * @status: status of the command asked, changes for each one diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 3f6428c..23825ff 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -257,6 +257,8 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { RX_HANDLER(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION, iwl_mvm_power_uapsd_misbehaving_ap_notif, false), RX_HANDLER(DTS_MEASUREMENT_NOTIFICATION, iwl_mvm_temp_notif, true), + RX_HANDLER_GRP(PHY_OPS_GROUP, DTS_MEASUREMENT_NOTIF_WIDE, + iwl_mvm_temp_notif, true), RX_HANDLER(TDLS_CHANNEL_SWITCH_NOTIFICATION, iwl_mvm_rx_tdls_notif, true), diff --git a/drivers/net/wireless/iwlwifi/mvm/tt.c b/drivers/net/wireless/iwlwifi/mvm/tt.c index fe7145c..58b762f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/iwlwifi/mvm/tt.c @@ -176,17 +176,27 @@ static int iwl_mvm_get_temp_cmd(struct iwl_mvm *mvm) struct iwl_dts_measurement_cmd cmd = { .flags = cpu_to_le32(DTS_TRIGGER_CMD_FLAGS_TEMP), }; + u32 cmdid; - return iwl_mvm_send_cmd_pdu(mvm, CMD_DTS_MEASUREMENT_TRIGGER, 0, + if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_WIDE_CMD_HDR)) + cmdid = iwl_cmd_id(CMD_DTS_MEASUREMENT_TRIGGER_WIDE, + PHY_OPS_GROUP, 0); + else + cmdid = CMD_DTS_MEASUREMENT_TRIGGER; + return iwl_mvm_send_cmd_pdu(mvm, cmdid, 0, sizeof(cmd), &cmd); } int iwl_mvm_get_temp(struct iwl_mvm *mvm) { struct iwl_notification_wait wait_temp_notif; - static const u16 temp_notif[] = { DTS_MEASUREMENT_NOTIFICATION }; + static u16 temp_notif[] = { WIDE_ID(PHY_OPS_GROUP, + DTS_MEASUREMENT_NOTIF_WIDE) }; int ret, temp; + if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_WIDE_CMD_HDR)) + temp_notif[0] = DTS_MEASUREMENT_NOTIFICATION; + lockdep_assert_held(&mvm->mutex); iwl_init_notification_wait(&mvm->notif_wait, &wait_temp_notif, -- cgit v0.10.2 From bb35dc14182c16e9646f6323cce25f3cfe3ba593 Mon Sep 17 00:00:00 2001 From: Moshe Harel Date: Sun, 6 Sep 2015 16:50:33 +0300 Subject: iwlwifi: nvm: force 1x1 antenna in Series 8000 This is a workaround to an OTP bug. In Series 8000 1x1, the OTP 0xA052 defines 2x2 antenna configuration. This workaround overrides the decision based on HW id and MIMO disabled bit which is correct in the OTP and set to disabled. Signed-off-by: Moshe Harel Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index 3b8e85e..d829849 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c @@ -580,13 +580,15 @@ static void iwl_set_hw_address_family_8000(struct device *dev, IWL_ERR_DEV(dev, "mac address is not found\n"); } +#define IWL_4165_DEVICE_ID 0x5501 + struct iwl_nvm_data * iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, const __le16 *nvm_hw, const __le16 *nvm_sw, const __le16 *nvm_calib, const __le16 *regulatory, const __le16 *mac_override, const __le16 *phy_sku, u8 tx_chains, u8 rx_chains, bool lar_fw_supported, - u32 mac_addr0, u32 mac_addr1) + u32 mac_addr0, u32 mac_addr1, u32 hw_id) { struct iwl_nvm_data *data; u32 sku; @@ -625,6 +627,17 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, (sku & NVM_SKU_CAP_11AC_ENABLE); data->sku_cap_mimo_disabled = sku & NVM_SKU_CAP_MIMO_DISABLE; + /* + * OTP 0x52 bug work around + * define antenna 1x1 according to MIMO disabled + */ + if (hw_id == IWL_4165_DEVICE_ID && data->sku_cap_mimo_disabled) { + data->valid_tx_ant = ANT_B; + data->valid_rx_ant = ANT_B; + tx_chains = ANT_B; + rx_chains = ANT_B; + } + data->n_hw_addrs = iwl_get_n_hw_addrs(cfg, nvm_sw); if (cfg->device_family != IWL_DEVICE_FAMILY_8000) { diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h index 822ba52..9f44d81 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h @@ -79,7 +79,7 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, const __le16 *nvm_calib, const __le16 *regulatory, const __le16 *mac_override, const __le16 *phy_sku, u8 tx_chains, u8 rx_chains, bool lar_fw_supported, - u32 mac_addr0, u32 mac_addr1); + u32 mac_addr0, u32 mac_addr1, u32 hw_id); /** * iwl_parse_mcc_info - parse MCC (mobile country code) info coming from FW diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index 328187d..0b3ffd5 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -316,7 +316,8 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib, regulatory, mac_override, phy_sku, mvm->fw->valid_tx_ant, mvm->fw->valid_rx_ant, - lar_enabled, mac_addr0, mac_addr1); + lar_enabled, mac_addr0, mac_addr1, + mvm->trans->hw_id); } #define MAX_NVM_FILE_LEN 16384 -- cgit v0.10.2 From a6449126518d908f0d706cce85c9bf243342b7b0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 1 Sep 2015 14:15:17 +0200 Subject: iwlwifi: mvm: correct skip-over-DTIM implementation The formula used in D0i3 should also be used in D3, instead of the hardcoded value. Additionally, the formula is actually wrong - if the calculation yields 0 then 1 should be used instead of disabling entirely. Also need to add 1 since the firmware needs 3 to skip 2, etc. To make all this clearer, centralize the calculation into a single function. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c index 4645877..21a858d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/iwlwifi/mvm/power.c @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH * * 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 @@ -33,6 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -306,13 +308,50 @@ static bool iwl_mvm_power_is_radar(struct ieee80211_vif *vif) return radar_detect; } +static void iwl_mvm_power_config_skip_dtim(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_mac_power_cmd *cmd, + bool host_awake) +{ + int dtimper = vif->bss_conf.dtim_period ?: 1; + int skip; + + /* disable, in case we're supposed to override */ + cmd->skip_dtim_periods = 0; + cmd->flags &= ~cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); + + if (iwl_mvm_power_is_radar(vif)) + return; + + if (dtimper >= 10) + return; + + /* TODO: check that multicast wake lock is off */ + + if (host_awake) { + if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_LP) + return; + skip = 2; + } else { + int dtimper_tu = dtimper * vif->bss_conf.beacon_int; + + if (WARN_ON(!dtimper_tu)) + return; + /* configure skip over dtim up to 306TU - 314 msec */ + skip = max_t(u8, 1, 306 / dtimper_tu); + } + + /* the firmware really expects "look at every X DTIMs", so add 1 */ + cmd->skip_dtim_periods = 1 + skip; + cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); +} + static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mac_power_cmd *cmd) { int dtimper, bi; int keep_alive; - bool radar_detect = false; struct iwl_mvm_vif *mvmvif __maybe_unused = iwl_mvm_vif_from_mac80211(vif); @@ -350,16 +389,8 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD; } - /* Check if radar detection is required on current channel */ - radar_detect = iwl_mvm_power_is_radar(vif); - - /* Check skip over DTIM conditions */ - if (!radar_detect && (dtimper < 10) && - (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP || - mvm->cur_ucode == IWL_UCODE_WOWLAN)) { - cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); - cmd->skip_dtim_periods = 3; - } + iwl_mvm_power_config_skip_dtim(mvm, vif, cmd, + mvm->cur_ucode != IWL_UCODE_WOWLAN); if (mvm->cur_ucode != IWL_UCODE_WOWLAN) { cmd->rx_data_timeout = @@ -964,24 +995,11 @@ int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm, return 0; iwl_mvm_power_build_cmd(mvm, vif, &cmd); - if (enable) { - /* configure skip over dtim up to 306TU - 314 msec */ - int dtimper = vif->bss_conf.dtim_period ?: 1; - int dtimper_tu = dtimper * vif->bss_conf.beacon_int; - bool radar_detect = iwl_mvm_power_is_radar(vif); - if (WARN_ON(!dtimper_tu)) - return 0; - - /* Check skip over DTIM conditions */ - /* TODO: check that multicast wake lock is off */ - if (!radar_detect && (dtimper < 10)) { - cmd.skip_dtim_periods = 306 / dtimper_tu; - if (cmd.skip_dtim_periods) - cmd.flags |= cpu_to_le16( - POWER_FLAGS_SKIP_OVER_DTIM_MSK); - } - } + /* when enabling D0i3, override the skip-over-dtim configuration */ + if (enable) + iwl_mvm_power_config_skip_dtim(mvm, vif, &cmd, false); + iwl_mvm_power_log(mvm, &cmd); #ifdef CONFIG_IWLWIFI_DEBUGFS memcpy(&mvmvif->mac_pwr_cmd, &cmd, sizeof(cmd)); -- cgit v0.10.2 From 7b9aebf0e507108daba3516f28cfd78fce80c176 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 8 Sep 2015 17:03:20 +0200 Subject: MAINTAINERS: iwlwifi: update contact email The ilw@linux.intel.com address is being phased out, replace it with the new address. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho diff --git a/MAINTAINERS b/MAINTAINERS index 310da42..77b2728 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5541,7 +5541,7 @@ F: drivers/net/wireless/iwlegacy/ INTEL WIRELESS WIFI LINK (iwlwifi) M: Johannes Berg M: Emmanuel Grumbach -M: Intel Linux Wireless +M: Intel Linux Wireless L: linux-wireless@vger.kernel.org W: http://intellinuxwireless.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi.git -- cgit v0.10.2 From acf9de3dfb2c9c00d94c3d614980006947ed2b7f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 4 Sep 2015 20:54:35 +0200 Subject: iwlwifi: enable tracing by default Tracing, if disabled at runtime, has very low overhead with great returns on debugging. It therefore makes sense to have it enabled by default (if the kernel enables EVENT_TRACING). Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig index aba0957..6e949df 100644 --- a/drivers/net/wireless/iwlwifi/Kconfig +++ b/drivers/net/wireless/iwlwifi/Kconfig @@ -142,6 +142,7 @@ config IWLWIFI_DEBUG_EXPERIMENTAL_UCODE config IWLWIFI_DEVICE_TRACING bool "iwlwifi device access tracing" depends on EVENT_TRACING + default y help Say Y here to trace all commands, including TX frames and IO accesses, sent to the device. If you say yes, iwlwifi will -- cgit v0.10.2 From 69191afef3c889992de643af7c318c9a8a1750c0 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Tue, 1 Sep 2015 18:50:22 +0300 Subject: iwlwifi: mvm: fix default disabled aggs in sta For the ADD_STA command, when the flag for aggregation disabling is set, there is a bitmap indicated what TIDs are disabling aggregations and what aren't. Currently, by default, all TIDs allow for aggregations since the value we begin with is 0. Change this default value to 0xffff so all TIDs don't allow aggregations until explicitly turned on. Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index fe2f538..18ca030 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -292,7 +292,7 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, /* HW restart, don't assume the memory has been zeroed */ atomic_set(&mvm->pending_frames[sta_id], 0); - mvm_sta->tid_disable_agg = 0; + mvm_sta->tid_disable_agg = 0xffff; /* No aggs at first */ mvm_sta->tfd_queue_msk = 0; /* allocate new queues for a TDLS station */ -- cgit v0.10.2 From f82c83397beac5c829bfcb3ea6bb4bfdbac79209 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Thu, 10 Sep 2015 12:54:38 +0300 Subject: iwlwifi: mvm: Correctly update MAC context on add/del station Commit "iwlwifi: mvm: don't ask beacons when AP vif and no assoc sta" directly called iwl_mvm_mac_ctxt_cmd_ap() to update the MAC context when adding/removing a station. However, this ignores the case that the vif is actually a P2P GO. Fix this by calling iwl_mvm_mac_ctxt_changed() that handles P2P GO case as well. Signed-off-by: Ilan Peer Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index 5af0090..3615650 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -1126,9 +1126,9 @@ static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm, ctxt_ap->beacon_template = cpu_to_le32(mvmvif->id); } -int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 action) +static int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 action) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mac_ctx_cmd cmd = {}; diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 4c497fe..d7275a5 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -2616,7 +2616,7 @@ static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw, if (mvm_sta->vif->type == NL80211_IFTYPE_AP) { mvmvif->ap_assoc_sta_count--; - iwl_mvm_mac_ctxt_cmd_ap(mvm, vif, FW_CTXT_ACTION_MODIFY); + iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); } mutex_unlock(&mvm->mutex); diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index cf1f514..5e02736 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -1129,10 +1129,6 @@ void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm, struct ieee80211_vif *vif); unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm, struct ieee80211_vif *exclude_vif); -int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 action); - /* Bindings */ int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 18ca030..4967990 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -277,7 +277,7 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, if (vif->type == NL80211_IFTYPE_AP) { mvmvif->ap_assoc_sta_count++; - iwl_mvm_mac_ctxt_cmd_ap(mvm, vif, FW_CTXT_ACTION_MODIFY); + iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); } spin_lock_init(&mvm_sta->lock); -- cgit v0.10.2 From bb7436093717d3ccaeaf28438be763b30a794ad8 Mon Sep 17 00:00:00 2001 From: Nicolas Iooss Date: Sat, 12 Sep 2015 12:04:51 +0200 Subject: iwlwifi: mvm: fix tof.h header guard Commit ce7929186a39 ("iwlwifi: mvm: add basic Time of Flight (802.11mc FTM) support") created drivers/net/wireless/iwlwifi/mvm/tof.h with a broken header guard: #ifndef __tof #define __tof_h__ ... #endif /* __tof_h__ */ Use __tof_h__ in the first line. Signed-off-by: Nicolas Iooss Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/tof.h b/drivers/net/wireless/iwlwifi/mvm/tof.h index 50ae8ad..9beebc3 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tof.h +++ b/drivers/net/wireless/iwlwifi/mvm/tof.h @@ -60,7 +60,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ -#ifndef __tof +#ifndef __tof_h__ #define __tof_h__ #include "fw-api-tof.h" -- cgit v0.10.2 From 7656d842de93fd2d2de7b403062cad757cadf1df Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 4 Oct 2015 21:08:07 -0700 Subject: tcp: fix fastopen races vs lockless listener There are multiple races that need fixes : 1) skb_get() + queue skb + kfree_skb() is racy An accept() can be done on another cpu, data consumed immediately. tcp_recvmsg() uses __kfree_skb() as it is assumed all skb found in socket receive queue are private. Then the kfree_skb() in tcp_rcv_state_process() uses an already freed skb 2) tcp_reqsk_record_syn() needs to be done before tcp_try_fastopen() for the same reasons. 3) We want to send the SYNACK before queueing child into accept queue, otherwise we might reintroduce the ooo issue fixed in commit 7c85af881044 ("tcp: avoid reorders for TFO passive connections") Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index 410ac48..93396bf 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -168,8 +168,6 @@ static struct sock *tcp_fastopen_create_child(struct sock *sk, TCP_TIMEOUT_INIT, TCP_RTO_MAX); atomic_set(&req->rsk_refcnt, 2); - /* Add the child socket directly into the accept queue */ - inet_csk_reqsk_queue_add(sk, req, child); /* Now finish processing the fastopen child socket. */ inet_csk(child)->icsk_af_ops->rebuild_header(child); @@ -178,12 +176,10 @@ static struct sock *tcp_fastopen_create_child(struct sock *sk, tcp_init_metrics(child); tcp_init_buffer_space(child); - /* Queue the data carried in the SYN packet. We need to first - * bump skb's refcnt because the caller will attempt to free it. - * Note that IPv6 might also have used skb_get() trick - * in tcp_v6_conn_request() to keep this SYN around (treq->pktopts) - * So we need to eventually get a clone of the packet, - * before inserting it in sk_receive_queue. + /* Queue the data carried in the SYN packet. + * We used to play tricky games with skb_get(). + * With lockless listener, it is a dead end. + * Do not think about it. * * XXX (TFO) - we honor a zero-payload TFO request for now, * (any reason not to?) but no need to queue the skb since @@ -191,12 +187,7 @@ static struct sock *tcp_fastopen_create_child(struct sock *sk, */ end_seq = TCP_SKB_CB(skb)->end_seq; if (end_seq != TCP_SKB_CB(skb)->seq + 1) { - struct sk_buff *skb2; - - if (unlikely(skb_shared(skb))) - skb2 = skb_clone(skb, GFP_ATOMIC); - else - skb2 = skb_get(skb); + struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); if (likely(skb2)) { skb_dst_drop(skb2); @@ -214,12 +205,9 @@ static struct sock *tcp_fastopen_create_child(struct sock *sk, } } tcp_rsk(req)->rcv_nxt = tp->rcv_nxt = end_seq; - sk->sk_data_ready(sk); - bh_unlock_sock(child); - /* Note: sock_put(child) will be done by tcp_conn_request() - * after SYNACK packet is sent. + /* tcp_conn_request() is sending the SYNACK, + * and queues the child into listener accept queue. */ - WARN_ON(!req->sk); return child; } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 2710875..a95c8eb 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6229,12 +6229,16 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, tcp_rsk(req)->txhash = net_tx_rndhash(); tcp_openreq_init_rwin(req, sk, dst); if (!want_cookie) { - fastopen_sk = tcp_try_fastopen(sk, skb, req, &foc, dst); tcp_reqsk_record_syn(sk, req, skb); + fastopen_sk = tcp_try_fastopen(sk, skb, req, &foc, dst); } if (fastopen_sk) { af_ops->send_synack(fastopen_sk, dst, &fl, req, skb_get_queue_mapping(skb), &foc, false); + /* Add the child socket directly into the accept queue */ + inet_csk_reqsk_queue_add(sk, req, fastopen_sk); + sk->sk_data_ready(sk); + bh_unlock_sock(fastopen_sk); sock_put(fastopen_sk); } else { tcp_rsk(req)->tfo_listener = false; -- cgit v0.10.2 From caf3f2676aaad395903d24a54e22f8ac4bc4823d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 4 Oct 2015 21:08:08 -0700 Subject: inet: ip_skb_dst_mtu() should use sk_fullsock() SYN_RECV & TIMEWAIT sockets are not full blown, do not even try to call ip_sk_use_pmtu() on them. Fixes: ca6fb0651883 ("tcp: attach SYNACK messages to request sockets instead of listener") Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/include/net/ip.h b/include/net/ip.h index 91a6b2c..aa78119 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -323,12 +323,15 @@ static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst, static inline unsigned int ip_skb_dst_mtu(const struct sk_buff *skb) { - if (!skb->sk || ip_sk_use_pmtu(skb->sk)) { + struct sock *sk = skb->sk; + + if (!sk || !sk_fullsock(sk) || ip_sk_use_pmtu(sk)) { bool forwarding = IPCB(skb)->flags & IPSKB_FORWARDED; + return ip_dst_mtu_maybe_forward(skb_dst(skb), forwarding); - } else { - return min(skb_dst(skb)->dev->mtu, IP_MAX_MTU); } + + return min(skb_dst(skb)->dev->mtu, IP_MAX_MTU); } u32 ip_idents_reserve(u32 hash, int segs); -- cgit v0.10.2 From e7eadb4de9e645e1b34539dc4128240b1e5f71dc Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 4 Oct 2015 21:08:09 -0700 Subject: ipv6: inet6_sk() should use sk_fullsock() SYN_RECV & TIMEWAIT sockets are not full blown, they do not have a pinet6 pointer. Fixes: ca6fb0651883 ("tcp: attach SYNACK messages to request sockets instead of listener") Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index f1f32af..0ef2a97 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -264,9 +264,9 @@ struct tcp6_timewait_sock { }; #if IS_ENABLED(CONFIG_IPV6) -static inline struct ipv6_pinfo * inet6_sk(const struct sock *__sk) +static inline struct ipv6_pinfo *inet6_sk(const struct sock *__sk) { - return inet_sk(__sk)->pinet6; + return sk_fullsock(__sk) ? inet_sk(__sk)->pinet6 : NULL; } static inline struct raw6_sock *raw6_sk(const struct sock *sk) -- cgit v0.10.2 From 004a5d0140ce1d05c1f5fce5df4baa2717a330e0 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 4 Oct 2015 21:08:10 -0700 Subject: net: use sk_fullsock() in __netdev_pick_tx() SYN_RECV & TIMEWAIT sockets are not full blown, they do not have a sk_dst_cache pointer. Fixes: ca6fb0651883 ("tcp: attach SYNACK messages to request sockets instead of listener") Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/include/net/request_sock.h b/include/net/request_sock.h index dd423d8..f836694 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -92,6 +92,7 @@ reqsk_alloc(const struct request_sock_ops *ops, struct sock *sk_listener) req->rsk_listener = sk_listener; req_to_sk(req)->sk_prot = sk_listener->sk_prot; sk_node_init(&req_to_sk(req)->sk_node); + sk_tx_queue_clear(req_to_sk(req)); req->saved_syn = NULL; /* Following is temporary. It is coupled with debugging * helpers in reqsk_put() & reqsk_free() diff --git a/net/core/dev.c b/net/core/dev.c index 323c04e..a229bf0 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2974,6 +2974,7 @@ static u16 __netdev_pick_tx(struct net_device *dev, struct sk_buff *skb) new_index = skb_tx_hash(dev, skb); if (queue_index != new_index && sk && + sk_fullsock(sk) && rcu_access_pointer(sk->sk_dst_cache)) sk_tx_queue_set(sk, new_index); -- cgit v0.10.2 From a1a5344ddbe8fd3e080013b317ac9a664490cfdf Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 4 Oct 2015 21:08:11 -0700 Subject: tcp: avoid two atomic ops for syncookies inet_reqsk_alloc() is used to allocate a temporary request in order to generate a SYNACK with a cookie. Then later, syncookie validation also uses a temporary request. These paths already took a reference on listener refcount, we can avoid a couple of atomic operations. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 47eb67b..f5bf731 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -245,7 +245,8 @@ static inline unsigned int __inet_ehashfn(const __be32 laddr, } struct request_sock *inet_reqsk_alloc(const struct request_sock_ops *ops, - struct sock *sk_listener); + struct sock *sk_listener, + bool attach_listener); static inline __u8 inet_sk_flowi_flags(const struct sock *sk) { diff --git a/include/net/request_sock.h b/include/net/request_sock.h index f836694..95ab5d7 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -80,7 +80,8 @@ static inline struct sock *req_to_sk(struct request_sock *req) } static inline struct request_sock * -reqsk_alloc(const struct request_sock_ops *ops, struct sock *sk_listener) +reqsk_alloc(const struct request_sock_ops *ops, struct sock *sk_listener, + bool attach_listener) { struct request_sock *req; @@ -88,8 +89,12 @@ reqsk_alloc(const struct request_sock_ops *ops, struct sock *sk_listener) if (req) { req->rsk_ops = ops; - sock_hold(sk_listener); - req->rsk_listener = sk_listener; + if (attach_listener) { + sock_hold(sk_listener); + req->rsk_listener = sk_listener; + } else { + req->rsk_listener = NULL; + } req_to_sk(req)->sk_prot = sk_listener->sk_prot; sk_node_init(&req_to_sk(req)->sk_node); sk_tx_queue_clear(req_to_sk(req)); diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 8910c95..8e99681 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -595,7 +595,7 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) goto drop; - req = inet_reqsk_alloc(&dccp_request_sock_ops, sk); + req = inet_reqsk_alloc(&dccp_request_sock_ops, sk, true); if (req == NULL) goto drop; diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 1361a3f..aed314f 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -319,7 +319,7 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb) if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) goto drop; - req = inet_reqsk_alloc(&dccp6_request_sock_ops, sk); + req = inet_reqsk_alloc(&dccp6_request_sock_ops, sk, true); if (req == NULL) goto drop; diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 729ceb5..8113c30 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -326,7 +326,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) goto out; ret = NULL; - req = inet_reqsk_alloc(&tcp_request_sock_ops, sk); /* for safety */ + req = inet_reqsk_alloc(&tcp_request_sock_ops, sk, false); /* for safety */ if (!req) goto out; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index a95c8eb..ddadb31 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6042,9 +6042,11 @@ static void tcp_openreq_init(struct request_sock *req, } struct request_sock *inet_reqsk_alloc(const struct request_sock_ops *ops, - struct sock *sk_listener) + struct sock *sk_listener, + bool attach_listener) { - struct request_sock *req = reqsk_alloc(ops, sk_listener); + struct request_sock *req = reqsk_alloc(ops, sk_listener, + attach_listener); if (req) { struct inet_request_sock *ireq = inet_rsk(req); @@ -6143,7 +6145,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, goto drop; } - req = inet_reqsk_alloc(rsk_ops, sk); + req = inet_reqsk_alloc(rsk_ops, sk, !want_cookie); if (!req) goto drop; diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 7606eba..f610b53 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -170,7 +170,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) goto out; ret = NULL; - req = inet_reqsk_alloc(&tcp6_request_sock_ops, sk); + req = inet_reqsk_alloc(&tcp6_request_sock_ops, sk, false); if (!req) goto out; -- cgit v0.10.2 From 0e884c78ee19e902f300ed147083c28a0c6302f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20N=C3=B8rlund?= Date: Wed, 30 Sep 2015 10:12:21 +0200 Subject: ipv4: L3 hash-based multipath MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the per-packet multipath with a hash-based multipath using source and destination address. Signed-off-by: Peter Nørlund Signed-off-by: David S. Miller diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 727d6e9..7a51fd8 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -79,7 +79,7 @@ struct fib_nh { unsigned char nh_scope; #ifdef CONFIG_IP_ROUTE_MULTIPATH int nh_weight; - int nh_power; + atomic_t nh_upper_bound; #endif #ifdef CONFIG_IP_ROUTE_CLASSID __u32 nh_tclassid; @@ -118,7 +118,7 @@ struct fib_info { #define fib_advmss fib_metrics[RTAX_ADVMSS-1] int fib_nhs; #ifdef CONFIG_IP_ROUTE_MULTIPATH - int fib_power; + int fib_weight; #endif struct rcu_head rcu; struct fib_nh fib_nh[0]; @@ -320,7 +320,15 @@ int ip_fib_check_default(__be32 gw, struct net_device *dev); int fib_sync_down_dev(struct net_device *dev, unsigned long event); int fib_sync_down_addr(struct net *net, __be32 local); int fib_sync_up(struct net_device *dev, unsigned int nh_flags); -void fib_select_multipath(struct fib_result *res); + +extern u32 fib_multipath_secret __read_mostly; + +static inline int fib_multipath_hash(__be32 saddr, __be32 daddr) +{ + return jhash_2words(saddr, daddr, fib_multipath_secret) >> 1; +} + +void fib_select_multipath(struct fib_result *res, int hash); /* Exported by fib_trie.c */ void fib_trie_init(void); diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 064bd3c..0c49d2f 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -57,8 +57,7 @@ static unsigned int fib_info_cnt; static struct hlist_head fib_info_devhash[DEVINDEX_HASHSIZE]; #ifdef CONFIG_IP_ROUTE_MULTIPATH - -static DEFINE_SPINLOCK(fib_multipath_lock); +u32 fib_multipath_secret __read_mostly; #define for_nexthops(fi) { \ int nhsel; const struct fib_nh *nh; \ @@ -532,7 +531,67 @@ errout: return ret; } -#endif +static void fib_rebalance(struct fib_info *fi) +{ + int total; + int w; + struct in_device *in_dev; + + if (fi->fib_nhs < 2) + return; + + total = 0; + for_nexthops(fi) { + if (nh->nh_flags & RTNH_F_DEAD) + continue; + + in_dev = __in_dev_get_rcu(nh->nh_dev); + + if (in_dev && + IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) && + nh->nh_flags & RTNH_F_LINKDOWN) + continue; + + total += nh->nh_weight; + } endfor_nexthops(fi); + + w = 0; + change_nexthops(fi) { + int upper_bound; + + in_dev = __in_dev_get_rcu(nexthop_nh->nh_dev); + + if (nexthop_nh->nh_flags & RTNH_F_DEAD) { + upper_bound = -1; + } else if (in_dev && + IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) && + nexthop_nh->nh_flags & RTNH_F_LINKDOWN) { + upper_bound = -1; + } else { + w += nexthop_nh->nh_weight; + upper_bound = DIV_ROUND_CLOSEST(2147483648LL * w, + total) - 1; + } + + atomic_set(&nexthop_nh->nh_upper_bound, upper_bound); + } endfor_nexthops(fi); + + net_get_random_once(&fib_multipath_secret, + sizeof(fib_multipath_secret)); +} + +static inline void fib_add_weight(struct fib_info *fi, + const struct fib_nh *nh) +{ + fi->fib_weight += nh->nh_weight; +} + +#else /* CONFIG_IP_ROUTE_MULTIPATH */ + +#define fib_rebalance(fi) do { } while (0) +#define fib_add_weight(fi, nh) do { } while (0) + +#endif /* CONFIG_IP_ROUTE_MULTIPATH */ static int fib_encap_match(struct net *net, u16 encap_type, struct nlattr *encap, @@ -1094,8 +1153,11 @@ struct fib_info *fib_create_info(struct fib_config *cfg) change_nexthops(fi) { fib_info_update_nh_saddr(net, nexthop_nh); + fib_add_weight(fi, nexthop_nh); } endfor_nexthops(fi) + fib_rebalance(fi); + link_it: ofi = fib_find_info(fi); if (ofi) { @@ -1317,12 +1379,6 @@ int fib_sync_down_dev(struct net_device *dev, unsigned long event) nexthop_nh->nh_flags |= RTNH_F_LINKDOWN; break; } -#ifdef CONFIG_IP_ROUTE_MULTIPATH - spin_lock_bh(&fib_multipath_lock); - fi->fib_power -= nexthop_nh->nh_power; - nexthop_nh->nh_power = 0; - spin_unlock_bh(&fib_multipath_lock); -#endif dead++; } #ifdef CONFIG_IP_ROUTE_MULTIPATH @@ -1345,6 +1401,8 @@ int fib_sync_down_dev(struct net_device *dev, unsigned long event) } ret++; } + + fib_rebalance(fi); } return ret; @@ -1467,20 +1525,15 @@ int fib_sync_up(struct net_device *dev, unsigned int nh_flags) !__in_dev_get_rtnl(dev)) continue; alive++; -#ifdef CONFIG_IP_ROUTE_MULTIPATH - spin_lock_bh(&fib_multipath_lock); - nexthop_nh->nh_power = 0; - nexthop_nh->nh_flags &= ~nh_flags; - spin_unlock_bh(&fib_multipath_lock); -#else nexthop_nh->nh_flags &= ~nh_flags; -#endif } endfor_nexthops(fi) if (alive > 0) { fi->fib_flags &= ~nh_flags; ret++; } + + fib_rebalance(fi); } return ret; @@ -1488,62 +1541,19 @@ int fib_sync_up(struct net_device *dev, unsigned int nh_flags) #ifdef CONFIG_IP_ROUTE_MULTIPATH -/* - * The algorithm is suboptimal, but it provides really - * fair weighted route distribution. - */ -void fib_select_multipath(struct fib_result *res) +void fib_select_multipath(struct fib_result *res, int hash) { struct fib_info *fi = res->fi; - struct in_device *in_dev; - int w; - - spin_lock_bh(&fib_multipath_lock); - if (fi->fib_power <= 0) { - int power = 0; - change_nexthops(fi) { - in_dev = __in_dev_get_rcu(nexthop_nh->nh_dev); - if (nexthop_nh->nh_flags & RTNH_F_DEAD) - continue; - if (in_dev && - IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) && - nexthop_nh->nh_flags & RTNH_F_LINKDOWN) - continue; - power += nexthop_nh->nh_weight; - nexthop_nh->nh_power = nexthop_nh->nh_weight; - } endfor_nexthops(fi); - fi->fib_power = power; - if (power <= 0) { - spin_unlock_bh(&fib_multipath_lock); - /* Race condition: route has just become dead. */ - res->nh_sel = 0; - return; - } - } - - /* w should be random number [0..fi->fib_power-1], - * it is pretty bad approximation. - */ - - w = jiffies % fi->fib_power; + for_nexthops(fi) { + if (hash > atomic_read(&nh->nh_upper_bound)) + continue; - change_nexthops(fi) { - if (!(nexthop_nh->nh_flags & RTNH_F_DEAD) && - nexthop_nh->nh_power) { - w -= nexthop_nh->nh_power; - if (w <= 0) { - nexthop_nh->nh_power--; - fi->fib_power--; - res->nh_sel = nhsel; - spin_unlock_bh(&fib_multipath_lock); - return; - } - } + res->nh_sel = nhsel; + return; } endfor_nexthops(fi); /* Race condition: route has just become dead. */ res->nh_sel = 0; - spin_unlock_bh(&fib_multipath_lock); } #endif diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 76ca4e7..0cca444 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1658,8 +1658,12 @@ static int ip_mkroute_input(struct sk_buff *skb, __be32 daddr, __be32 saddr, u32 tos) { #ifdef CONFIG_IP_ROUTE_MULTIPATH - if (res->fi && res->fi->fib_nhs > 1) - fib_select_multipath(res); + if (res->fi && res->fi->fib_nhs > 1) { + int h; + + h = fib_multipath_hash(saddr, daddr); + fib_select_multipath(res, h); + } #endif /* create a routing cache entry */ @@ -2189,8 +2193,12 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4) } #ifdef CONFIG_IP_ROUTE_MULTIPATH - if (res.fi->fib_nhs > 1 && fl4->flowi4_oif == 0) - fib_select_multipath(&res); + if (res.fi->fib_nhs > 1 && fl4->flowi4_oif == 0) { + int h; + + h = fib_multipath_hash(fl4->saddr, fl4->daddr); + fib_select_multipath(&res, h); + } else #endif if (!res.prefixlen && -- cgit v0.10.2 From 79a131592dbb81a2dba208622a2ffbfc53f28bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20N=C3=B8rlund?= Date: Wed, 30 Sep 2015 10:12:22 +0200 Subject: ipv4: ICMP packet inspection for multipath MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ICMP packets are inspected to let them route together with the flow they belong to, minimizing the chance that a problematic path will affect flows on other paths, and so that anycast environments can work with ECMP. Signed-off-by: Peter Nørlund Signed-off-by: David S. Miller diff --git a/include/net/route.h b/include/net/route.h index e211dc1..d32cb76 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -113,7 +114,15 @@ struct in_device; int ip_rt_init(void); void rt_cache_flush(struct net *net); void rt_flush_dev(struct net_device *dev); -struct rtable *__ip_route_output_key(struct net *, struct flowi4 *flp); +struct rtable *__ip_route_output_key_hash(struct net *, struct flowi4 *flp, + int mp_hash); + +static inline struct rtable *__ip_route_output_key(struct net *net, + struct flowi4 *flp) +{ + return __ip_route_output_key_hash(net, flp, -1); +} + struct rtable *ip_route_output_flow(struct net *, struct flowi4 *flp, const struct sock *sk); struct dst_entry *ipv4_blackhole_route(struct net *net, diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 6b96dee..36e2697 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -440,6 +440,22 @@ out_unlock: icmp_xmit_unlock(sk); } +#ifdef CONFIG_IP_ROUTE_MULTIPATH + +/* Source and destination is swapped. See ip_multipath_icmp_hash */ +static int icmp_multipath_hash_skb(const struct sk_buff *skb) +{ + const struct iphdr *iph = ip_hdr(skb); + + return fib_multipath_hash(iph->daddr, iph->saddr); +} + +#else + +#define icmp_multipath_hash_skb(skb) (-1) + +#endif + static struct rtable *icmp_route_lookup(struct net *net, struct flowi4 *fl4, struct sk_buff *skb_in, @@ -464,7 +480,8 @@ static struct rtable *icmp_route_lookup(struct net *net, fl4->flowi4_oif = l3mdev_master_ifindex(skb_in->dev); security_skb_classify_flow(skb_in, flowi4_to_flowi(fl4)); - rt = __ip_route_output_key(net, fl4); + rt = __ip_route_output_key_hash(net, fl4, + icmp_multipath_hash_skb(skb_in)); if (IS_ERR(rt)) return rt; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 0cca444..54297d3 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1651,6 +1651,48 @@ out: return err; } +#ifdef CONFIG_IP_ROUTE_MULTIPATH + +/* To make ICMP packets follow the right flow, the multipath hash is + * calculated from the inner IP addresses in reverse order. + */ +static int ip_multipath_icmp_hash(struct sk_buff *skb) +{ + const struct iphdr *outer_iph = ip_hdr(skb); + struct icmphdr _icmph; + const struct icmphdr *icmph; + struct iphdr _inner_iph; + const struct iphdr *inner_iph; + + if (unlikely((outer_iph->frag_off & htons(IP_OFFSET)) != 0)) + goto standard_hash; + + icmph = skb_header_pointer(skb, outer_iph->ihl * 4, sizeof(_icmph), + &_icmph); + if (!icmph) + goto standard_hash; + + if (icmph->type != ICMP_DEST_UNREACH && + icmph->type != ICMP_REDIRECT && + icmph->type != ICMP_TIME_EXCEEDED && + icmph->type != ICMP_PARAMETERPROB) { + goto standard_hash; + } + + inner_iph = skb_header_pointer(skb, + outer_iph->ihl * 4 + sizeof(_icmph), + sizeof(_inner_iph), &_inner_iph); + if (!inner_iph) + goto standard_hash; + + return fib_multipath_hash(inner_iph->daddr, inner_iph->saddr); + +standard_hash: + return fib_multipath_hash(outer_iph->saddr, outer_iph->daddr); +} + +#endif /* CONFIG_IP_ROUTE_MULTIPATH */ + static int ip_mkroute_input(struct sk_buff *skb, struct fib_result *res, const struct flowi4 *fl4, @@ -1661,7 +1703,10 @@ static int ip_mkroute_input(struct sk_buff *skb, if (res->fi && res->fi->fib_nhs > 1) { int h; - h = fib_multipath_hash(saddr, daddr); + if (unlikely(ip_hdr(skb)->protocol == IPPROTO_ICMP)) + h = ip_multipath_icmp_hash(skb); + else + h = fib_multipath_hash(saddr, daddr); fib_select_multipath(res, h); } #endif @@ -2030,7 +2075,8 @@ add: * Major route resolver routine. */ -struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4) +struct rtable *__ip_route_output_key_hash(struct net *net, struct flowi4 *fl4, + int mp_hash) { struct net_device *dev_out = NULL; __u8 tos = RT_FL_TOS(fl4); @@ -2194,10 +2240,9 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4) #ifdef CONFIG_IP_ROUTE_MULTIPATH if (res.fi->fib_nhs > 1 && fl4->flowi4_oif == 0) { - int h; - - h = fib_multipath_hash(fl4->saddr, fl4->daddr); - fib_select_multipath(&res, h); + if (mp_hash < 0) + mp_hash = fib_multipath_hash(fl4->saddr, fl4->daddr); + fib_select_multipath(&res, mp_hash); } else #endif @@ -2220,7 +2265,7 @@ out: rcu_read_unlock(); return rth; } -EXPORT_SYMBOL_GPL(__ip_route_output_key); +EXPORT_SYMBOL_GPL(__ip_route_output_key_hash); static struct dst_entry *ipv4_blackhole_dst_check(struct dst_entry *dst, u32 cookie) { -- cgit v0.10.2 From be7ccdc36ba4815ca71b0ac6df898237a912b3ac Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 30 Sep 2015 13:26:31 +0200 Subject: net: fec: avoid timespec use The fec_ptp_enable_pps uses an open-coded implementation of ns_to_timespec, which will be removed eventually as it is not y2038-safe on 32-bit architectures. Two more instances of the same code in this file were already converted to use the safe ns_to_timespec64 in commit 6630514fcee ("ptp: fec: use helpers for converting ns to timespec"), this changes the last one as well. The seconds portion here is actually unused and we could just remove the timespec variable, but using ns_to_timespec64 can still be better as the implementation can be hand-optimized in the future. Signed-off-by: Arnd Bergmann Cc: Richard Cochran Cc: Fugang Duan Cc: Luwei Zhou Cc: Frank Li Acked-by: Richard Cochran Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c index 1543cf0..f9e7446 100644 --- a/drivers/net/ethernet/freescale/fec_ptp.c +++ b/drivers/net/ethernet/freescale/fec_ptp.c @@ -112,9 +112,8 @@ static int fec_ptp_enable_pps(struct fec_enet_private *fep, uint enable) unsigned long flags; u32 val, tempval; int inc; - struct timespec ts; + struct timespec64 ts; u64 ns; - u32 remainder; val = 0; if (!(fep->hwts_tx_en || fep->hwts_rx_en)) { @@ -163,8 +162,7 @@ static int fec_ptp_enable_pps(struct fec_enet_private *fep, uint enable) tempval = readl(fep->hwp + FEC_ATIME); /* Convert the ptp local counter to 1588 timestamp */ ns = timecounter_cyc2time(&fep->tc, tempval); - ts.tv_sec = div_u64_rem(ns, 1000000000ULL, &remainder); - ts.tv_nsec = remainder; + ts = ns_to_timespec64(ns); /* The tempval is less than 3 seconds, and so val is less than * 4 seconds. No overflow for 32bit calculation. -- cgit v0.10.2 From 0a6241551d20e982dba8fc6c88b0a021456ea7b4 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 30 Sep 2015 13:26:32 +0200 Subject: net: stmmac: avoid using timespec We want to deprecate the use of 'struct timespec' on 32-bit architectures, as it is will overflow in 2038. The stmmac driver uses it to read the current time, and can simply be changed to use ktime_get_real_ts64() instead. Because of hardware limitations, there is still an overflow in year 2106, which we cannot really avoid, but this documents the overflow. Signed-off-by: Arnd Bergmann Cc: Giuseppe Cavallaro Cc: Richard Cochran Acked-by: Richard Cochran Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 934143e..64d8aa4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -424,7 +424,7 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr) { struct stmmac_priv *priv = netdev_priv(dev); struct hwtstamp_config config; - struct timespec now; + struct timespec64 now; u64 temp = 0; u32 ptp_v2 = 0; u32 tstamp_all = 0; @@ -621,8 +621,10 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr) priv->default_addend); /* initialize system time */ - getnstimeofday(&now); - priv->hw->ptp->init_systime(priv->ioaddr, now.tv_sec, + ktime_get_real_ts64(&now); + + /* lower 32 bits of tv_sec are safe until y2106 */ + priv->hw->ptp->init_systime(priv->ioaddr, (u32)now.tv_sec, now.tv_nsec); } -- cgit v0.10.2 From 40c9b0796d46523fffb93e46ed8c691456146743 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 30 Sep 2015 13:26:33 +0200 Subject: net: igb: avoid using timespec We want to deprecate the use of 'struct timespec' on 32-bit architectures, as it is will overflow in 2038. The igb driver uses it to read the current time, and can simply be changed to use ktime_get_real_ts64() instead. Because of hardware limitations, there is still an overflow in year 2106, which we cannot really avoid, but this documents the overflow. Signed-off-by: Arnd Bergmann Cc: Jeff Kirsher Cc: intel-wired-lan@lists.osuosl.org Reviewed-by: Richard Cochran Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index 212d668..1a2f1cc 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -444,8 +444,8 @@ struct igb_adapter { struct ptp_pin_desc sdp_config[IGB_N_SDP]; struct { - struct timespec start; - struct timespec period; + struct timespec64 start; + struct timespec64 period; } perout[IGB_N_PEROUT]; char fw_version[32]; diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index ba019fc..7e62675 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -5392,7 +5392,7 @@ static void igb_tsync_interrupt(struct igb_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; struct ptp_clock_event event; - struct timespec ts; + struct timespec64 ts; u32 ack = 0, tsauxc, sec, nsec, tsicr = rd32(E1000_TSICR); if (tsicr & TSINTR_SYS_WRAP) { @@ -5412,10 +5412,11 @@ static void igb_tsync_interrupt(struct igb_adapter *adapter) if (tsicr & TSINTR_TT0) { spin_lock(&adapter->tmreg_lock); - ts = timespec_add(adapter->perout[0].start, - adapter->perout[0].period); + ts = timespec64_add(adapter->perout[0].start, + adapter->perout[0].period); + /* u32 conversion of tv_sec is safe until y2106 */ wr32(E1000_TRGTTIML0, ts.tv_nsec); - wr32(E1000_TRGTTIMH0, ts.tv_sec); + wr32(E1000_TRGTTIMH0, (u32)ts.tv_sec); tsauxc = rd32(E1000_TSAUXC); tsauxc |= TSAUXC_EN_TT0; wr32(E1000_TSAUXC, tsauxc); @@ -5426,10 +5427,10 @@ static void igb_tsync_interrupt(struct igb_adapter *adapter) if (tsicr & TSINTR_TT1) { spin_lock(&adapter->tmreg_lock); - ts = timespec_add(adapter->perout[1].start, - adapter->perout[1].period); + ts = timespec64_add(adapter->perout[1].start, + adapter->perout[1].period); wr32(E1000_TRGTTIML1, ts.tv_nsec); - wr32(E1000_TRGTTIMH1, ts.tv_sec); + wr32(E1000_TRGTTIMH1, (u32)ts.tv_sec); tsauxc = rd32(E1000_TSAUXC); tsauxc |= TSAUXC_EN_TT1; wr32(E1000_TSAUXC, tsauxc); diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c index 5982f28..c44df87 100644 --- a/drivers/net/ethernet/intel/igb/igb_ptp.c +++ b/drivers/net/ethernet/intel/igb/igb_ptp.c @@ -143,7 +143,7 @@ static void igb_ptp_write_i210(struct igb_adapter *adapter, * sub-nanosecond resolution. */ wr32(E1000_SYSTIML, ts->tv_nsec); - wr32(E1000_SYSTIMH, ts->tv_sec); + wr32(E1000_SYSTIMH, (u32)ts->tv_sec); } /** @@ -479,7 +479,7 @@ static int igb_ptp_feature_enable_i210(struct ptp_clock_info *ptp, struct e1000_hw *hw = &igb->hw; u32 tsauxc, tsim, tsauxc_mask, tsim_mask, trgttiml, trgttimh, freqout; unsigned long flags; - struct timespec ts; + struct timespec64 ts; int use_freq = 0, pin = -1; s64 ns; @@ -523,14 +523,14 @@ static int igb_ptp_feature_enable_i210(struct ptp_clock_info *ptp, } ts.tv_sec = rq->perout.period.sec; ts.tv_nsec = rq->perout.period.nsec; - ns = timespec_to_ns(&ts); + ns = timespec64_to_ns(&ts); ns = ns >> 1; if (on && ns <= 70000000LL) { if (ns < 8LL) return -EINVAL; use_freq = 1; } - ts = ns_to_timespec(ns); + ts = ns_to_timespec64(ns); if (rq->perout.index == 1) { if (use_freq) { tsauxc_mask = TSAUXC_EN_CLK1 | TSAUXC_ST1; -- cgit v0.10.2 From e253fb74d66428597bd272b2614e8564b882b14c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 30 Sep 2015 13:26:34 +0200 Subject: mwifiex: use ktime_get_real for timestamping The mwifiex_11n_aggregate_pkt() function creates a ktime_t from a timeval returned by do_gettimeofday, which is slow and causes an overflow in 2038 on 32-bit architectures. This solves both problems by using the appropriate ktime_get_real() function. Signed-off-by: Arnd Bergmann Cc: Amitkumar Karwar Cc: Nishant Sarmukadam Cc: Kalle Valo Cc: linux-wireless@vger.kernel.org Signed-off-by: David S. Miller diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c index f7c7172..78853c5 100644 --- a/drivers/net/wireless/mwifiex/11n_aggr.c +++ b/drivers/net/wireless/mwifiex/11n_aggr.c @@ -173,7 +173,6 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, int pad = 0, aggr_num = 0, ret; struct mwifiex_tx_param tx_param; struct txpd *ptx_pd = NULL; - struct timeval tv; int headroom = adapter->iface_type == MWIFIEX_USB ? 0 : INTF_HEADER_LEN; skb_src = skb_peek(&pra_list->skb_head); @@ -203,8 +202,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_AGGR_PKT; skb_aggr->priority = skb_src->priority; - do_gettimeofday(&tv); - skb_aggr->tstamp = timeval_to_ktime(tv); + skb_aggr->tstamp = ktime_get_real(); do { /* Check if AMSDU can accommodate this MSDU */ -- cgit v0.10.2 From 52f4f91893955c359885d022b2142e2326f020a1 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 30 Sep 2015 13:26:35 +0200 Subject: mwifiex: avoid gettimeofday in ba_threshold setting mwifiex_get_random_ba_threshold() uses a complex homegrown implementation to generate a pseudo-random number from the current time as returned from do_gettimeofday(). This currently requires two 32-bit divisions plus a couple of other computations that are eventually discarded as only eight bits of the microsecond portion are used at all. We could replace this with a call to get_random_bytes(), but that might drain the entropy pool too fast if this is called for each packet. Instead, this patch converts it to use ktime_get_ns(), which is a bit faster than do_gettimeofday(), and then uses a similar algorithm as before, but in a way that takes both the nanosecond and second portion into account for slightly-more-but-still-not-very-random pseudorandom number. Signed-off-by: Arnd Bergmann Cc: Amitkumar Karwar Cc: Nishant Sarmukadam Cc: Kalle Valo Cc: linux-wireless@vger.kernel.org Signed-off-by: David S. Miller diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 173d366..878d358 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -117,22 +117,15 @@ mwifiex_wmm_allocate_ralist_node(struct mwifiex_adapter *adapter, const u8 *ra) */ static u8 mwifiex_get_random_ba_threshold(void) { - u32 sec, usec; - struct timeval ba_tstamp; - u8 ba_threshold; - + u64 ns; /* setup ba_packet_threshold here random number between * [BA_SETUP_PACKET_OFFSET, * BA_SETUP_PACKET_OFFSET+BA_SETUP_MAX_PACKET_THRESHOLD-1] */ + ns = ktime_get_ns(); + ns += (ns >> 32) + (ns >> 16); - do_gettimeofday(&ba_tstamp); - sec = (ba_tstamp.tv_sec & 0xFFFF) + (ba_tstamp.tv_sec >> 16); - usec = (ba_tstamp.tv_usec & 0xFFFF) + (ba_tstamp.tv_usec >> 16); - ba_threshold = (((sec << 16) + usec) % BA_SETUP_MAX_PACKET_THRESHOLD) - + BA_SETUP_PACKET_OFFSET; - - return ba_threshold; + return ((u8)ns % BA_SETUP_MAX_PACKET_THRESHOLD) + BA_SETUP_PACKET_OFFSET; } /* -- cgit v0.10.2 From 84b00607aeb8f139a11c93036e1c0ee03dde5634 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 30 Sep 2015 13:26:36 +0200 Subject: mac80211: use ktime_get_seconds The mac80211 code uses ktime_get_ts to measure the connected time. As this uses monotonic time, it is y2038 safe on 32-bit systems, but we still want to deprecate the use of 'timespec' because most other users are broken. This changes the code to use ktime_get_seconds() instead, which avoids the timespec structure and is slightly more efficient. Signed-off-by: Arnd Bergmann Cc: Johannes Berg Cc: linux-wireless@vger.kernel.org Signed-off-by: David S. Miller diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 64f1936..c364445 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -303,7 +303,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct ieee80211_hw *hw = &local->hw; struct sta_info *sta; - struct timespec uptime; int i; sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp); @@ -339,8 +338,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, /* Mark TID as unreserved */ sta->reserved_tid = IEEE80211_TID_UNRESERVED; - ktime_get_ts(&uptime); - sta->last_connected = uptime.tv_sec; + sta->last_connected = ktime_get_seconds(); ewma_signal_init(&sta->avg_signal); for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++) ewma_signal_init(&sta->chain_signal_avg[i]); @@ -1813,7 +1811,6 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; struct rate_control_ref *ref = NULL; - struct timespec uptime; u32 thr = 0; int i, ac; @@ -1838,8 +1835,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) BIT(NL80211_STA_INFO_RX_DROP_MISC) | BIT(NL80211_STA_INFO_BEACON_LOSS); - ktime_get_ts(&uptime); - sinfo->connected_time = uptime.tv_sec - sta->last_connected; + sinfo->connected_time = ktime_get_seconds() - sta->last_connected; sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); if (!(sinfo->filled & (BIT(NL80211_STA_INFO_TX_BYTES64) | -- cgit v0.10.2 From 70ba07b675b53782ad366ebc4d3a00eab9a06bc9 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 30 Sep 2015 17:32:01 +0200 Subject: atm: remove 'struct zatm_t_hist' The zatm_t_hist structure is not used anywhere in the kernel, but is exported to user space. As we are trying to eliminate uses of time_t in the kernel for y2038 compatibility, the current definition triggers checking tools because it contains 'struct timeval'. As pointed out by Chas Williams, the only user of this structure was the ZATM_GETHIST ioctl command that has been removed a long time ago, and we can remove the structure as well without breaking any user space. Signed-off-by: Arnd Bergmann Cc: Chas Williams <3chas3@gmail.com> Cc: linux-atm-general@lists.sourceforge.net Signed-off-by: David S. Miller diff --git a/include/uapi/linux/atm_zatm.h b/include/uapi/linux/atm_zatm.h index 10f0fa2..9c9c6ad 100644 --- a/include/uapi/linux/atm_zatm.h +++ b/include/uapi/linux/atm_zatm.h @@ -35,12 +35,6 @@ struct zatm_pool_req { struct zatm_pool_info info; /* actual information */ }; -struct zatm_t_hist { - struct timeval real; /* real (wall-clock) time */ - struct timeval expected; /* expected real time */ -}; - - #define ZATM_OAM_POOL 0 /* free buffer pool for OAM cells */ #define ZATM_AAL0_POOL 1 /* free buffer pool for AAL0 cells */ #define ZATM_AAL5_POOL_BASE 2 /* first AAL5 free buffer pool */ -- cgit v0.10.2 From f6389ecbc5f3ddc5860aab22bd7f7e1a8aeb3165 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 30 Sep 2015 13:26:38 +0200 Subject: nfnetlink: use y2038 safe timestamp The __build_packet_message function fills a nfulnl_msg_packet_timestamp structure that uses 64-bit seconds and is therefore y2038 safe, but it uses an intermediate 'struct timespec' which is not. This trivially changes the code to use 'struct timespec64' instead, to correct the result on 32-bit architectures. Signed-off-by: Arnd Bergmann Cc: Pablo Neira Ayuso Cc: Patrick McHardy Cc: Jozsef Kadlecsik Cc: netfilter-devel@vger.kernel.org Cc: coreteam@netfilter.org Acked-by: Pablo Neira Ayuso Signed-off-by: David S. Miller diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index 4670821..cc2300f 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -538,9 +538,9 @@ __build_packet_message(struct nfnl_log_net *log, if (skb->tstamp.tv64) { struct nfulnl_msg_packet_timestamp ts; - struct timeval tv = ktime_to_timeval(skb->tstamp); - ts.sec = cpu_to_be64(tv.tv_sec); - ts.usec = cpu_to_be64(tv.tv_usec); + struct timespec64 kts = ktime_to_timespec64(skb->tstamp); + ts.sec = cpu_to_be64(kts.tv_sec); + ts.usec = cpu_to_be64(kts.tv_nsec / NSEC_PER_USEC); if (nla_put(inst->skb, NFULA_TIMESTAMP, sizeof(ts), &ts)) goto nla_put_failure; -- cgit v0.10.2 From 3dd7669f1f13772d0a846dee58379399f163729c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 30 Sep 2015 13:26:39 +0200 Subject: ipv6: use ktime_t for internal timestamps The ipv6 mip6 implementation is one of only a few users of the skb_get_timestamp() function in the kernel, which is both unsafe on 32-bit architectures because of the 2038 overflow, and slightly less efficient than the skb_get_ktime() based approach. This converts the function call and the mip6_report_rate_limiter structure that stores the time stamp, eliminating all uses of timeval in the ipv6 code. Signed-off-by: Arnd Bergmann Cc: Alexey Kuznetsov Cc: James Morris Cc: Hideaki YOSHIFUJI Cc: Patrick McHardy Signed-off-by: David S. Miller diff --git a/net/ipv6/mip6.c b/net/ipv6/mip6.c index b9779d4..60c79a0 100644 --- a/net/ipv6/mip6.c +++ b/net/ipv6/mip6.c @@ -118,7 +118,7 @@ static int mip6_mh_filter(struct sock *sk, struct sk_buff *skb) struct mip6_report_rate_limiter { spinlock_t lock; - struct timeval stamp; + ktime_t stamp; int iif; struct in6_addr src; struct in6_addr dst; @@ -184,20 +184,18 @@ static int mip6_destopt_output(struct xfrm_state *x, struct sk_buff *skb) return 0; } -static inline int mip6_report_rl_allow(struct timeval *stamp, +static inline int mip6_report_rl_allow(ktime_t stamp, const struct in6_addr *dst, const struct in6_addr *src, int iif) { int allow = 0; spin_lock_bh(&mip6_report_rl.lock); - if (mip6_report_rl.stamp.tv_sec != stamp->tv_sec || - mip6_report_rl.stamp.tv_usec != stamp->tv_usec || + if (!ktime_equal(mip6_report_rl.stamp, stamp) || mip6_report_rl.iif != iif || !ipv6_addr_equal(&mip6_report_rl.src, src) || !ipv6_addr_equal(&mip6_report_rl.dst, dst)) { - mip6_report_rl.stamp.tv_sec = stamp->tv_sec; - mip6_report_rl.stamp.tv_usec = stamp->tv_usec; + mip6_report_rl.stamp = stamp; mip6_report_rl.iif = iif; mip6_report_rl.src = *src; mip6_report_rl.dst = *dst; @@ -216,7 +214,7 @@ static int mip6_destopt_reject(struct xfrm_state *x, struct sk_buff *skb, struct ipv6_destopt_hao *hao = NULL; struct xfrm_selector sel; int offset; - struct timeval stamp; + ktime_t stamp; int err = 0; if (unlikely(fl6->flowi6_proto == IPPROTO_MH && @@ -230,9 +228,9 @@ static int mip6_destopt_reject(struct xfrm_state *x, struct sk_buff *skb, (skb_network_header(skb) + offset); } - skb_get_timestamp(skb, &stamp); + stamp = skb_get_ktime(skb); - if (!mip6_report_rl_allow(&stamp, &ipv6_hdr(skb)->daddr, + if (!mip6_report_rl_allow(stamp, &ipv6_hdr(skb)->daddr, hao ? &hao->addr : &ipv6_hdr(skb)->saddr, opt->iif)) goto out; -- cgit v0.10.2 From 3ef0a25bf9ef318615c810e24d244d55c09806d7 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 30 Sep 2015 13:26:40 +0200 Subject: net: sctp: avoid incorrect time_t use We want to avoid using time_t in the kernel because of the y2038 overflow problem. The use in sctp is not for storing seconds at all, but instead uses microseconds and is passed as 32-bit on all machines. This patch changes the type to u32, which better fits the use. Signed-off-by: Arnd Bergmann Cc: Vlad Yasevich Cc: Neil Horman Cc: linux-sctp@vger.kernel.org Acked-by: Neil Horman Acked-by: Marcelo Ricardo Leitner Signed-off-by: David S. Miller diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 7954c52..763e06a 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -2494,7 +2494,7 @@ static int sctp_process_param(struct sctp_association *asoc, __u16 sat; int retval = 1; sctp_scope_t scope; - time_t stale; + u32 stale; struct sctp_af *af; union sctp_addr_param *addr_param; struct sctp_transport *t; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index d7eaa73..6f46aa1 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -2306,7 +2306,7 @@ static sctp_disposition_t sctp_sf_do_5_2_6_stale(struct net *net, sctp_cmd_seq_t *commands) { struct sctp_chunk *chunk = arg; - time_t stale; + u32 stale; sctp_cookie_preserve_param_t bht; sctp_errhdr_t *err; struct sctp_chunk *reply; -- cgit v0.10.2 From ac8cfc7bb836835bd68c3ab9da242747e9df9542 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 30 Sep 2015 06:18:23 -0700 Subject: tcp: restore fastopen operations I accidentally cleared fastopenq.max_qlen in reqsk_queue_alloc() while max_qlen can be set before listen() is called, using TCP_FASTOPEN socket option for example. Fixes: 0536fcc039a8 ("tcp: prepare fastopen code for upcoming listener changes") Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/net/core/request_sock.c b/net/core/request_sock.c index 15c8538..5d26056 100644 --- a/net/core/request_sock.c +++ b/net/core/request_sock.c @@ -45,7 +45,6 @@ void reqsk_queue_alloc(struct request_sock_queue *queue) queue->fastopenq.rskq_rst_head = NULL; queue->fastopenq.rskq_rst_tail = NULL; queue->fastopenq.qlen = 0; - queue->fastopenq.max_qlen = 0; queue->rskq_accept_head = NULL; } -- cgit v0.10.2 From 3947d78a547845fa5a129a39ce0abac2352f7a3a Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Wed, 30 Sep 2015 08:52:38 -0500 Subject: amd-xgbe: Remove an unneeded semicolon on a switch statement Remove an unneeded semicolon at the end of a switch statement block. Reported-by: kbuild test robot Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index a4473d8..553e251 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -2224,7 +2224,7 @@ static u64 xgbe_mmc_read(struct xgbe_prv_data *pdata, unsigned int reg_lo) default: read_hi = false; - }; + } val = XGMAC_IOREAD(pdata, reg_lo); -- cgit v0.10.2 From 47f2e6c27553d9a164bb0067f4c1f9bbba388c2e Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Wed, 30 Sep 2015 08:52:45 -0500 Subject: amd-xgbe: Use proper DT / ACPI precedence checking Device tree presence takes precedence over ACPI in the device_* APIs. The amd-xgbe driver should follow the same precedence. Update the check on whether to use DT / ACPI to follow this. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index e83bd76..7dd8933 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -371,7 +371,7 @@ 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 = (!pdata->adev || acpi_disabled) ? 0 : 1; + pdata->use_acpi = dev->of_node ? 0 : 1; phy_pdev = xgbe_get_phy_pdev(pdata); if (!phy_pdev) { -- cgit v0.10.2 From 349fb2d7008caa752919e394fb202545b5f72c67 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Wed, 30 Sep 2015 08:52:51 -0500 Subject: amd-xgbe: Add ethtool support for setting the msglevel Provide the ethtool functions to support getting and setting the msglevel for the driver. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c index 59e090e..29db64b 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c @@ -388,6 +388,20 @@ static void xgbe_get_drvinfo(struct net_device *netdev, drvinfo->n_stats = XGBE_STATS_COUNT; } +static u32 xgbe_get_msglevel(struct net_device *netdev) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + + return pdata->msg_enable; +} + +static void xgbe_set_msglevel(struct net_device *netdev, u32 msglevel) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + + pdata->msg_enable = msglevel; +} + static int xgbe_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec) { @@ -594,6 +608,8 @@ 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, .get_link = ethtool_op_get_link, .get_coalesce = xgbe_get_coalesce, .set_coalesce = xgbe_set_coalesce, -- cgit v0.10.2 From e5dd8b81107881f8928378a38f6e47aa2da62c37 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Wed, 30 Sep 2015 08:52:57 -0500 Subject: amd-xgbe: Add ethtool error and debug messages Add error and dynamic debug messages to various ethtool functions in the driver while also removing the DBGPR debug print calls. Also, change the message level for some error messages from alert to err. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c index 29db64b..95b6373 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c @@ -187,8 +187,6 @@ static void xgbe_get_strings(struct net_device *netdev, u32 stringset, u8 *data) { int i; - DBGPR("-->%s\n", __func__); - switch (stringset) { case ETH_SS_STATS: for (i = 0; i < XGBE_STATS_COUNT; i++) { @@ -198,8 +196,6 @@ static void xgbe_get_strings(struct net_device *netdev, u32 stringset, u8 *data) } break; } - - DBGPR("<--%s\n", __func__); } static void xgbe_get_ethtool_stats(struct net_device *netdev, @@ -209,23 +205,17 @@ static void xgbe_get_ethtool_stats(struct net_device *netdev, u8 *stat; int i; - DBGPR("-->%s\n", __func__); - pdata->hw_if.read_mmc_stats(pdata); for (i = 0; i < XGBE_STATS_COUNT; i++) { stat = (u8 *)pdata + xgbe_gstring_stats[i].stat_offset; *data++ = *(u64 *)stat; } - - DBGPR("<--%s\n", __func__); } static int xgbe_get_sset_count(struct net_device *netdev, int stringset) { int ret; - DBGPR("-->%s\n", __func__); - switch (stringset) { case ETH_SS_STATS: ret = XGBE_STATS_COUNT; @@ -235,8 +225,6 @@ static int xgbe_get_sset_count(struct net_device *netdev, int stringset) ret = -EOPNOTSUPP; } - DBGPR("<--%s\n", __func__); - return ret; } @@ -245,13 +233,9 @@ static void xgbe_get_pauseparam(struct net_device *netdev, { struct xgbe_prv_data *pdata = netdev_priv(netdev); - DBGPR("-->xgbe_get_pauseparam\n"); - pause->autoneg = pdata->phy.pause_autoneg; pause->tx_pause = pdata->phy.tx_pause; pause->rx_pause = pdata->phy.rx_pause; - - DBGPR("<--xgbe_get_pauseparam\n"); } static int xgbe_set_pauseparam(struct net_device *netdev, @@ -260,13 +244,11 @@ static int xgbe_set_pauseparam(struct net_device *netdev, struct xgbe_prv_data *pdata = netdev_priv(netdev); int ret = 0; - DBGPR("-->xgbe_set_pauseparam\n"); - - DBGPR(" autoneg = %d, tx_pause = %d, rx_pause = %d\n", - pause->autoneg, pause->tx_pause, pause->rx_pause); - - if (pause->autoneg && (pdata->phy.autoneg != AUTONEG_ENABLE)) + if (pause->autoneg && (pdata->phy.autoneg != AUTONEG_ENABLE)) { + netdev_err(netdev, + "autoneg disabled, pause autoneg not avialable\n"); return -EINVAL; + } pdata->phy.pause_autoneg = pause->autoneg; pdata->phy.tx_pause = pause->tx_pause; @@ -286,8 +268,6 @@ static int xgbe_set_pauseparam(struct net_device *netdev, if (netif_running(netdev)) ret = pdata->phy_if.phy_config_aneg(pdata); - DBGPR("<--xgbe_set_pauseparam\n"); - return ret; } @@ -296,8 +276,6 @@ static int xgbe_get_settings(struct net_device *netdev, { struct xgbe_prv_data *pdata = netdev_priv(netdev); - DBGPR("-->xgbe_get_settings\n"); - cmd->phy_address = pdata->phy.address; cmd->supported = pdata->phy.supported; @@ -311,8 +289,6 @@ static int xgbe_get_settings(struct net_device *netdev, cmd->port = PORT_NONE; cmd->transceiver = XCVR_INTERNAL; - DBGPR("<--xgbe_get_settings\n"); - return 0; } @@ -323,16 +299,20 @@ static int xgbe_set_settings(struct net_device *netdev, u32 speed; int ret; - DBGPR("-->xgbe_set_settings\n"); - speed = ethtool_cmd_speed(cmd); - if (cmd->phy_address != pdata->phy.address) + if (cmd->phy_address != pdata->phy.address) { + netdev_err(netdev, "invalid phy address %hhu\n", + cmd->phy_address); return -EINVAL; + } if ((cmd->autoneg != AUTONEG_ENABLE) && - (cmd->autoneg != AUTONEG_DISABLE)) + (cmd->autoneg != AUTONEG_DISABLE)) { + netdev_err(netdev, "unsupported autoneg %hhu\n", + cmd->autoneg); return -EINVAL; + } if (cmd->autoneg == AUTONEG_DISABLE) { switch (speed) { @@ -341,16 +321,27 @@ static int xgbe_set_settings(struct net_device *netdev, case SPEED_1000: break; default: + netdev_err(netdev, "unsupported speed %u\n", speed); return -EINVAL; } - if (cmd->duplex != DUPLEX_FULL) + if (cmd->duplex != DUPLEX_FULL) { + netdev_err(netdev, "unsupported duplex %hhu\n", + cmd->duplex); return -EINVAL; + } } + netif_dbg(pdata, link, netdev, + "requested advertisement %#x, phy supported %#x\n", + cmd->advertising, pdata->phy.supported); + cmd->advertising &= pdata->phy.supported; - if ((cmd->autoneg == AUTONEG_ENABLE) && !cmd->advertising) + if ((cmd->autoneg == AUTONEG_ENABLE) && !cmd->advertising) { + netdev_err(netdev, + "unsupported requested advertisement\n"); return -EINVAL; + } ret = 0; pdata->phy.autoneg = cmd->autoneg; @@ -366,8 +357,6 @@ static int xgbe_set_settings(struct net_device *netdev, if (netif_running(netdev)) ret = pdata->phy_if.phy_config_aneg(pdata); - DBGPR("<--xgbe_set_settings\n"); - return ret; } @@ -407,8 +396,6 @@ static int xgbe_get_coalesce(struct net_device *netdev, { struct xgbe_prv_data *pdata = netdev_priv(netdev); - DBGPR("-->xgbe_get_coalesce\n"); - memset(ec, 0, sizeof(struct ethtool_coalesce)); ec->rx_coalesce_usecs = pdata->rx_usecs; @@ -416,8 +403,6 @@ static int xgbe_get_coalesce(struct net_device *netdev, ec->tx_max_coalesced_frames = pdata->tx_frames; - DBGPR("<--xgbe_get_coalesce\n"); - return 0; } @@ -429,8 +414,6 @@ static int xgbe_set_coalesce(struct net_device *netdev, unsigned int rx_frames, rx_riwt, rx_usecs; unsigned int tx_frames; - DBGPR("-->xgbe_set_coalesce\n"); - /* Check for not supported parameters */ if ((ec->rx_coalesce_usecs_irq) || (ec->rx_max_coalesced_frames_irq) || @@ -450,8 +433,10 @@ static int xgbe_set_coalesce(struct net_device *netdev, (ec->rx_max_coalesced_frames_high) || (ec->tx_coalesce_usecs_high) || (ec->tx_max_coalesced_frames_high) || - (ec->rate_sample_interval)) + (ec->rate_sample_interval)) { + netdev_err(netdev, "unsupported coalescing parameter\n"); return -EOPNOTSUPP; + } rx_riwt = hw_if->usec_to_riwt(pdata, ec->rx_coalesce_usecs); rx_usecs = ec->rx_coalesce_usecs; @@ -463,13 +448,13 @@ static int xgbe_set_coalesce(struct net_device *netdev, /* Check the bounds of values for Rx */ if (rx_riwt > XGMAC_MAX_DMA_RIWT) { - netdev_alert(netdev, "rx-usec is limited to %d usecs\n", - hw_if->riwt_to_usec(pdata, XGMAC_MAX_DMA_RIWT)); + netdev_err(netdev, "rx-usec is limited to %d usecs\n", + hw_if->riwt_to_usec(pdata, XGMAC_MAX_DMA_RIWT)); return -EINVAL; } if (rx_frames > pdata->rx_desc_count) { - netdev_alert(netdev, "rx-frames is limited to %d frames\n", - pdata->rx_desc_count); + netdev_err(netdev, "rx-frames is limited to %d frames\n", + pdata->rx_desc_count); return -EINVAL; } @@ -477,8 +462,8 @@ static int xgbe_set_coalesce(struct net_device *netdev, /* Check the bounds of values for Tx */ if (tx_frames > pdata->tx_desc_count) { - netdev_alert(netdev, "tx-frames is limited to %d frames\n", - pdata->tx_desc_count); + netdev_err(netdev, "tx-frames is limited to %d frames\n", + pdata->tx_desc_count); return -EINVAL; } @@ -490,8 +475,6 @@ static int xgbe_set_coalesce(struct net_device *netdev, pdata->tx_frames = tx_frames; hw_if->config_tx_coalesce(pdata); - DBGPR("<--xgbe_set_coalesce\n"); - return 0; } @@ -553,8 +536,10 @@ static int xgbe_set_rxfh(struct net_device *netdev, const u32 *indir, struct xgbe_hw_if *hw_if = &pdata->hw_if; unsigned int ret; - if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) + if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) { + netdev_err(netdev, "unsupported hash function\n"); return -EOPNOTSUPP; + } if (indir) { ret = hw_if->set_rss_lookup_table(pdata, indir); -- cgit v0.10.2 From 9c439e4b730f0d519821527afd697e6af99a67ab Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Wed, 30 Sep 2015 08:53:03 -0500 Subject: amd-xgbe: Simplify calculation and setting of queue fifos The calculation of the Tx and Rx fifo sizes can be calculated rather than hardcoded in a switch statement. Additionally, the per-queue fifo sizes can be calculated rather than hardcoded using if/else if statements that can possibly underutilize the available fifo area. Change the code to calculate the fifo sizes and the per-queue fifo sizes to simplify the code and make best use of the available fifo. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index 553e251..4551224 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -1940,84 +1940,31 @@ static void xgbe_config_mtl_mode(struct xgbe_prv_data *pdata) static unsigned int xgbe_calculate_per_queue_fifo(unsigned int fifo_size, unsigned int queue_count) { - unsigned int q_fifo_size = 0; - enum xgbe_mtl_fifo_size p_fifo = XGMAC_MTL_FIFO_SIZE_256; + unsigned int q_fifo_size; + unsigned int p_fifo; - /* Calculate Tx/Rx fifo share per queue */ - switch (fifo_size) { - case 0: - q_fifo_size = XGBE_FIFO_SIZE_B(128); - break; - case 1: - q_fifo_size = XGBE_FIFO_SIZE_B(256); - break; - case 2: - q_fifo_size = XGBE_FIFO_SIZE_B(512); - break; - case 3: - q_fifo_size = XGBE_FIFO_SIZE_KB(1); - break; - case 4: - q_fifo_size = XGBE_FIFO_SIZE_KB(2); - break; - case 5: - q_fifo_size = XGBE_FIFO_SIZE_KB(4); - break; - case 6: - q_fifo_size = XGBE_FIFO_SIZE_KB(8); - break; - case 7: - q_fifo_size = XGBE_FIFO_SIZE_KB(16); - break; - case 8: - q_fifo_size = XGBE_FIFO_SIZE_KB(32); - break; - case 9: - q_fifo_size = XGBE_FIFO_SIZE_KB(64); - break; - case 10: - q_fifo_size = XGBE_FIFO_SIZE_KB(128); - break; - case 11: - q_fifo_size = XGBE_FIFO_SIZE_KB(256); - break; - } + /* Calculate the configured fifo size */ + q_fifo_size = 1 << (fifo_size + 7); - /* The configured value is not the actual amount of fifo RAM */ + /* 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); q_fifo_size = q_fifo_size / queue_count; - /* Set the queue fifo size programmable value */ - if (q_fifo_size >= XGBE_FIFO_SIZE_KB(256)) - p_fifo = XGMAC_MTL_FIFO_SIZE_256K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(128)) - p_fifo = XGMAC_MTL_FIFO_SIZE_128K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(64)) - p_fifo = XGMAC_MTL_FIFO_SIZE_64K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(32)) - p_fifo = XGMAC_MTL_FIFO_SIZE_32K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(16)) - p_fifo = XGMAC_MTL_FIFO_SIZE_16K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(8)) - p_fifo = XGMAC_MTL_FIFO_SIZE_8K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(4)) - p_fifo = XGMAC_MTL_FIFO_SIZE_4K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(2)) - p_fifo = XGMAC_MTL_FIFO_SIZE_2K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(1)) - p_fifo = XGMAC_MTL_FIFO_SIZE_1K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_B(512)) - p_fifo = XGMAC_MTL_FIFO_SIZE_512; - else if (q_fifo_size >= XGBE_FIFO_SIZE_B(256)) - p_fifo = XGMAC_MTL_FIFO_SIZE_256; + /* Each increment in the queue fifo size represents 256 bytes of + * fifo, with 0 representing 256 bytes. Distribute the fifo equally + * between the queues. + */ + p_fifo = q_fifo_size / 256; + if (p_fifo) + p_fifo--; return p_fifo; } static void xgbe_config_tx_fifo_size(struct xgbe_prv_data *pdata) { - enum xgbe_mtl_fifo_size fifo_size; + unsigned int fifo_size; unsigned int i; fifo_size = xgbe_calculate_per_queue_fifo(pdata->hw_feat.tx_fifo_size, @@ -2033,7 +1980,7 @@ static void xgbe_config_tx_fifo_size(struct xgbe_prv_data *pdata) static void xgbe_config_rx_fifo_size(struct xgbe_prv_data *pdata) { - enum xgbe_mtl_fifo_size fifo_size; + unsigned int fifo_size; unsigned int i; fifo_size = xgbe_calculate_per_queue_fifo(pdata->hw_feat.rx_fifo_size, diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 8c9d01e..5f9a1ab 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -209,8 +209,6 @@ #define XGMAC_IOCTL_CONTEXT 2 #define XGBE_FIFO_MAX 81920 -#define XGBE_FIFO_SIZE_B(x) (x) -#define XGBE_FIFO_SIZE_KB(x) (x * 1024) #define XGBE_TC_MIN_QUANTUM 10 @@ -483,20 +481,6 @@ enum xgbe_int_state { XGMAC_INT_STATE_RESTORE, }; -enum xgbe_mtl_fifo_size { - XGMAC_MTL_FIFO_SIZE_256 = 0x00, - XGMAC_MTL_FIFO_SIZE_512 = 0x01, - XGMAC_MTL_FIFO_SIZE_1K = 0x03, - XGMAC_MTL_FIFO_SIZE_2K = 0x07, - XGMAC_MTL_FIFO_SIZE_4K = 0x0f, - XGMAC_MTL_FIFO_SIZE_8K = 0x1f, - XGMAC_MTL_FIFO_SIZE_16K = 0x3f, - XGMAC_MTL_FIFO_SIZE_32K = 0x7f, - XGMAC_MTL_FIFO_SIZE_64K = 0xff, - XGMAC_MTL_FIFO_SIZE_128K = 0x1ff, - XGMAC_MTL_FIFO_SIZE_256K = 0x3ff, -}; - enum xgbe_speed { XGBE_SPEED_1000 = 0, XGBE_SPEED_2500, -- cgit v0.10.2 From 72c9ac4e1f4faf1f4e7252319ab8af2517891ae5 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Wed, 30 Sep 2015 08:53:10 -0500 Subject: amd-xgbe: Add receive buffer unavailable statistic Add a statistic that tracks how many times an interrupt is generated for a receive buffer not being available to the hardware which prevents the hardware from being able to DMA the received data. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index aae9d5e..24212d2 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -360,6 +360,9 @@ static irqreturn_t xgbe_isr(int irq, void *data) } } + if (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, RBU)) + pdata->ext_stats.rx_buffer_unavailable++; + /* Restart the device on a Fatal Bus Error */ if (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, FBE)) schedule_work(&pdata->restart_work); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c index 95b6373..204fb3a 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c @@ -179,6 +179,7 @@ static const struct xgbe_stats xgbe_gstring_stats[] = { XGMAC_MMC_STAT("rx_watchdog_errors", rxwatchdogerror), XGMAC_MMC_STAT("rx_pause_frames", rxpauseframes), XGMAC_EXT_STAT("rx_split_header_packets", rx_split_header_packets), + XGMAC_EXT_STAT("rx_buffer_unavailable", rx_buffer_unavailable), }; #define XGBE_STATS_COUNT ARRAY_SIZE(xgbe_gstring_stats) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 5f9a1ab..a5f5a78 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -582,6 +582,7 @@ struct xgbe_mmc_stats { struct xgbe_ext_stats { u64 tx_tso_packets; u64 rx_split_header_packets; + u64 rx_buffer_unavailable; }; struct xgbe_hw_if { -- cgit v0.10.2 From afb43e8a0af18fa959e9ef09abfa969c3c83f4ef Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Wed, 30 Sep 2015 08:53:16 -0500 Subject: amd-xgbe: Use device workqueue instead of system workqueue The driver creates, flushes and destroys a device workqueue but queues work to the system workqueue. Switch from using the system workqueue to the device workqueue. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index 24212d2..14bad8c 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -365,7 +365,7 @@ static irqreturn_t xgbe_isr(int irq, void *data) /* Restart the device on a Fatal Bus Error */ if (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, FBE)) - schedule_work(&pdata->restart_work); + queue_work(pdata->dev_workqueue, &pdata->restart_work); /* Clear all interrupt signals */ XGMAC_DMA_IOWRITE(channel, DMA_CH_SR, dma_ch_isr); @@ -387,7 +387,8 @@ static irqreturn_t xgbe_isr(int irq, void *data) /* Read Tx Timestamp to clear interrupt */ pdata->tx_tstamp = hw_if->get_tx_tstamp(pdata); - schedule_work(&pdata->tx_tstamp_work); + queue_work(pdata->dev_workqueue, + &pdata->tx_tstamp_work); } } } @@ -453,7 +454,7 @@ static void xgbe_service_timer(unsigned long data) { struct xgbe_prv_data *pdata = (struct xgbe_prv_data *)data; - schedule_work(&pdata->service_work); + queue_work(pdata->dev_workqueue, &pdata->service_work); mod_timer(&pdata->service_timer, jiffies + HZ); } @@ -894,7 +895,7 @@ static int xgbe_start(struct xgbe_prv_data *pdata) netif_tx_start_all_queues(netdev); xgbe_start_timers(pdata); - schedule_work(&pdata->service_work); + queue_work(pdata->dev_workqueue, &pdata->service_work); DBGPR("<--xgbe_start\n"); @@ -1536,7 +1537,7 @@ static void xgbe_tx_timeout(struct net_device *netdev) struct xgbe_prv_data *pdata = netdev_priv(netdev); netdev_warn(netdev, "tx timeout, device restarting\n"); - schedule_work(&pdata->restart_work); + queue_work(pdata->dev_workqueue, &pdata->restart_work); } static struct rtnl_link_stats64 *xgbe_get_stats64(struct net_device *netdev, -- cgit v0.10.2 From 50789845cfc37d5331b56e1a566ddc95aeac0a7d Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Wed, 30 Sep 2015 08:53:22 -0500 Subject: amd-xgbe: Remove the XGBE_LINK state bit The XGBE_LINK bit is used just to determine whether to call the netif_carrier_on/off functions. Rather than define and use this bit, just call the functions. The netif_carrier_ok function can be used in place of checking the XGBE_LINK bit in the future. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index 9088c3a..4460580 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -1115,8 +1115,7 @@ static void xgbe_phy_status(struct xgbe_prv_data *pdata) unsigned int reg, link_aneg; if (test_bit(XGBE_LINK_ERR, &pdata->dev_state)) { - if (test_and_clear_bit(XGBE_LINK, &pdata->dev_state)) - netif_carrier_off(pdata->netdev); + netif_carrier_off(pdata->netdev); pdata->phy.link = 0; goto adjust_link; @@ -1142,10 +1141,7 @@ static void xgbe_phy_status(struct xgbe_prv_data *pdata) if (test_bit(XGBE_LINK_INIT, &pdata->dev_state)) clear_bit(XGBE_LINK_INIT, &pdata->dev_state); - if (!test_bit(XGBE_LINK, &pdata->dev_state)) { - set_bit(XGBE_LINK, &pdata->dev_state); - netif_carrier_on(pdata->netdev); - } + netif_carrier_on(pdata->netdev); } else { if (test_bit(XGBE_LINK_INIT, &pdata->dev_state)) { xgbe_check_link_timeout(pdata); @@ -1156,10 +1152,7 @@ static void xgbe_phy_status(struct xgbe_prv_data *pdata) xgbe_phy_status_aneg(pdata); - if (test_bit(XGBE_LINK, &pdata->dev_state)) { - clear_bit(XGBE_LINK, &pdata->dev_state); - netif_carrier_off(pdata->netdev); - } + netif_carrier_off(pdata->netdev); } adjust_link: @@ -1179,8 +1172,7 @@ static void xgbe_phy_stop(struct xgbe_prv_data *pdata) devm_free_irq(pdata->dev, pdata->an_irq, pdata); pdata->phy.link = 0; - if (test_and_clear_bit(XGBE_LINK, &pdata->dev_state)) - netif_carrier_off(pdata->netdev); + netif_carrier_off(pdata->netdev); xgbe_phy_adjust_link(pdata); } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index a5f5a78..e234b99 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -459,7 +459,6 @@ struct xgbe_channel { enum xgbe_state { XGBE_DOWN, - XGBE_LINK, XGBE_LINK_INIT, XGBE_LINK_ERR, }; -- cgit v0.10.2 From 3b20fc389705a4c959adebc494578cb99bb8be9e Mon Sep 17 00:00:00 2001 From: Sowmini Varadhan Date: Wed, 30 Sep 2015 16:54:07 -0400 Subject: RDS: Use a single TCP socket for both send and receive. Commit f711a6ae062c ("net/rds: RDS-TCP: Always create a new rds_sock for an incoming connection.") modified rds-tcp so that an incoming SYN would ignore an existing "client" TCP connection which had the local port set to the transient port. The motivation for ignoring the existing "client" connection in f711a6ae was to avoid race conditions and an endless duel of reconnect attempts triggered by a restart/abort of one of the nodes in the TCP connection. However, having separate sockets for active and passive sides is avoidable, and the simpler model of a single TCP socket for both send and receives of all RDS connections associated with that tcp socket makes for easier observability. We avoid the race conditions from f711a6ae by attempting reconnects in rds_conn_shutdown if, and only if, the (new) c_outgoing bit is set for RDS_TRANS_TCP. The c_outgoing bit is initialized in __rds_conn_create(). A side-effect of re-using the client rds_connection for an incoming SYN is the potential of encountering duelling SYNs, i.e., we have an outgoing RDS_CONN_CONNECTING socket when we get the incoming SYN. The logic to arbitrate this criss-crossing SYN exchange in rds_tcp_accept_one() has been modified to emulate the BGP state machine: the smaller IP address should back off from the connection attempt. Signed-off-by: Sowmini Varadhan Signed-off-by: David S. Miller diff --git a/net/rds/connection.c b/net/rds/connection.c index 49adeef..d456403 100644 --- a/net/rds/connection.c +++ b/net/rds/connection.c @@ -128,10 +128,7 @@ static struct rds_connection *__rds_conn_create(struct net *net, struct rds_transport *loop_trans; unsigned long flags; int ret; - struct rds_transport *otrans = trans; - if (!is_outgoing && otrans->t_type == RDS_TRANS_TCP) - goto new_conn; rcu_read_lock(); conn = rds_conn_lookup(net, head, laddr, faddr, trans); if (conn && conn->c_loopback && conn->c_trans != &rds_loop_transport && @@ -147,7 +144,6 @@ static struct rds_connection *__rds_conn_create(struct net *net, if (conn) goto out; -new_conn: conn = kmem_cache_zalloc(rds_conn_slab, gfp); if (!conn) { conn = ERR_PTR(-ENOMEM); @@ -207,6 +203,7 @@ new_conn: atomic_set(&conn->c_state, RDS_CONN_DOWN); conn->c_send_gen = 0; + conn->c_outgoing = (is_outgoing ? 1 : 0); conn->c_reconnect_jiffies = 0; INIT_DELAYED_WORK(&conn->c_send_w, rds_send_worker); INIT_DELAYED_WORK(&conn->c_recv_w, rds_recv_worker); @@ -243,22 +240,13 @@ new_conn: /* Creating normal conn */ struct rds_connection *found; - if (!is_outgoing && otrans->t_type == RDS_TRANS_TCP) - found = NULL; - else - found = rds_conn_lookup(net, head, laddr, faddr, trans); + found = rds_conn_lookup(net, head, laddr, faddr, trans); if (found) { trans->conn_free(conn->c_transport_data); kmem_cache_free(rds_conn_slab, conn); conn = found; } else { - if ((is_outgoing && otrans->t_type == RDS_TRANS_TCP) || - (otrans->t_type != RDS_TRANS_TCP)) { - /* Only the active side should be added to - * reconnect list for TCP. - */ - hlist_add_head_rcu(&conn->c_hash_node, head); - } + hlist_add_head_rcu(&conn->c_hash_node, head); rds_cong_add_conn(conn); rds_conn_count++; } @@ -337,7 +325,9 @@ void rds_conn_shutdown(struct rds_connection *conn) rcu_read_lock(); if (!hlist_unhashed(&conn->c_hash_node)) { rcu_read_unlock(); - rds_queue_reconnect(conn); + if (conn->c_trans->t_type != RDS_TRANS_TCP || + conn->c_outgoing == 1) + rds_queue_reconnect(conn); } else { rcu_read_unlock(); } diff --git a/net/rds/rds.h b/net/rds/rds.h index afb4048..b4c7ac0 100644 --- a/net/rds/rds.h +++ b/net/rds/rds.h @@ -86,7 +86,9 @@ struct rds_connection { struct hlist_node c_hash_node; __be32 c_laddr; __be32 c_faddr; - unsigned int c_loopback:1; + unsigned int c_loopback:1, + c_outgoing:1, + c_pad_to_32:30; struct rds_connection *c_passive; struct rds_cong_map *c_lcong; diff --git a/net/rds/tcp_listen.c b/net/rds/tcp_listen.c index 444d78d..1d90240 100644 --- a/net/rds/tcp_listen.c +++ b/net/rds/tcp_listen.c @@ -110,28 +110,24 @@ int rds_tcp_accept_one(struct socket *sock) goto out; } /* An incoming SYN request came in, and TCP just accepted it. - * We always create a new conn for listen side of TCP, and do not - * add it to the c_hash_list. * * If the client reboots, this conn will need to be cleaned up. * rds_tcp_state_change() will do that cleanup */ rs_tcp = (struct rds_tcp_connection *)conn->c_transport_data; - WARN_ON(!rs_tcp || rs_tcp->t_sock); - - /* - * see the comment above rds_queue_delayed_reconnect() - */ - if (!rds_conn_transition(conn, RDS_CONN_DOWN, RDS_CONN_CONNECTING)) { - if (rds_conn_state(conn) == RDS_CONN_UP) - rds_tcp_stats_inc(s_tcp_listen_closed_stale); - else - rds_tcp_stats_inc(s_tcp_connect_raced); - rds_conn_drop(conn); + if (rs_tcp->t_sock && + ntohl(inet->inet_saddr) < ntohl(inet->inet_daddr)) { + struct sock *nsk = new_sock->sk; + + nsk->sk_user_data = NULL; + nsk->sk_prot->disconnect(nsk, 0); + tcp_done(nsk); + new_sock = NULL; ret = 0; goto out; } + rds_conn_transition(conn, RDS_CONN_DOWN, RDS_CONN_CONNECTING); rds_tcp_set_callbacks(new_sock, conn); rds_connect_complete(conn); new_sock = NULL; -- cgit v0.10.2 From 1edd6a14d24f21b8b478970c63a243a08e2b55b0 Mon Sep 17 00:00:00 2001 From: Sowmini Varadhan Date: Wed, 30 Sep 2015 16:54:08 -0400 Subject: RDS-TCP: Do not bloat sndbuf/rcvbuf in rds_tcp_tune Using the value of RDS_TCP_DEFAULT_BUFSIZE (128K) clobbers efficient use of TSO because it inflates the size_goal that is computed in tcp_sendmsg/tcp_sendpage and skews packet latency, and the default values for these parameters actually results in significantly better performance. In request-response tests using rds-stress with a packet size of 100K with 16 threads (test parameters -q 100000 -a 256 -t16 -d16) between a single pair of IP addresses achieves a throughput of 6-8 Gbps. Without this patch, throughput maxes at 2-3 Gbps under equivalent conditions on these platforms. Signed-off-by: Sowmini Varadhan Signed-off-by: David S. Miller diff --git a/net/rds/tcp.c b/net/rds/tcp.c index c42b60b..9d6ddba 100644 --- a/net/rds/tcp.c +++ b/net/rds/tcp.c @@ -67,21 +67,13 @@ void rds_tcp_nonagle(struct socket *sock) set_fs(oldfs); } +/* All module specific customizations to the RDS-TCP socket should be done in + * rds_tcp_tune() and applied after socket creation. In general these + * customizations should be tunable via module_param() + */ void rds_tcp_tune(struct socket *sock) { - struct sock *sk = sock->sk; - rds_tcp_nonagle(sock); - - /* - * We're trying to saturate gigabit with the default, - * see svc_sock_setbufsize(). - */ - lock_sock(sk); - sk->sk_sndbuf = RDS_TCP_DEFAULT_BUFSIZE; - sk->sk_rcvbuf = RDS_TCP_DEFAULT_BUFSIZE; - sk->sk_userlocks |= SOCK_SNDBUF_LOCK|SOCK_RCVBUF_LOCK; - release_sock(sk); } u32 rds_tcp_snd_nxt(struct rds_tcp_connection *tc) -- cgit v0.10.2 From 76b29ef120f5b845f862de08b92c7d2317b50907 Mon Sep 17 00:00:00 2001 From: Sowmini Varadhan Date: Wed, 30 Sep 2015 16:54:09 -0400 Subject: RDS-TCP: Set up MSG_MORE and MSG_SENDPAGE_NOTLAST as appropriate in rds_tcp_xmit For the same reasons as commit 2f5338442425 ("tcp: allow splice() to build full TSO packets") and commit 35f9c09fe9c7 ("tcp: tcp_sendpages() should call tcp_push() once"), rds_tcp_xmit may have multiple pages to send, so use the MSG_MORE and MSG_SENDPAGE_NOTLAST as hints to tcp_sendpage() Signed-off-by: Sowmini Varadhan Signed-off-by: David S. Miller diff --git a/net/rds/tcp_send.c b/net/rds/tcp_send.c index 53b17ca..2894e60 100644 --- a/net/rds/tcp_send.c +++ b/net/rds/tcp_send.c @@ -83,6 +83,7 @@ int rds_tcp_xmit(struct rds_connection *conn, struct rds_message *rm, struct rds_tcp_connection *tc = conn->c_transport_data; int done = 0; int ret = 0; + int more; if (hdr_off == 0) { /* @@ -116,12 +117,15 @@ int rds_tcp_xmit(struct rds_connection *conn, struct rds_message *rm, goto out; } + more = rm->data.op_nents > 1 ? (MSG_MORE | MSG_SENDPAGE_NOTLAST) : 0; while (sg < rm->data.op_nents) { + int flags = MSG_DONTWAIT | MSG_NOSIGNAL | more; + ret = tc->t_sock->ops->sendpage(tc->t_sock, sg_page(&rm->data.op_sg[sg]), rm->data.op_sg[sg].offset + off, rm->data.op_sg[sg].length - off, - MSG_DONTWAIT|MSG_NOSIGNAL); + flags); rdsdebug("tcp sendpage %p:%u:%u ret %d\n", (void *)sg_page(&rm->data.op_sg[sg]), rm->data.op_sg[sg].offset + off, rm->data.op_sg[sg].length - off, ret); @@ -134,6 +138,8 @@ int rds_tcp_xmit(struct rds_connection *conn, struct rds_message *rm, off = 0; sg++; } + if (sg == rm->data.op_nents - 1) + more = 0; } out: -- cgit v0.10.2 From 8119c018000bcc2d56e74f5459b7ff1902f96ff8 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Thu, 1 Oct 2015 13:48:45 +0530 Subject: cxgb4 : Update T4/T5/T6 register ranges Update T4/T5/T6 adapter register ranges so that it doesn't read non existent registers when dumped using ethtool Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 4480625..dc6ce31 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -699,50 +699,107 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) { static const unsigned int t4_reg_ranges[] = { 0x1008, 0x1108, - 0x1180, 0x11b4, + 0x1180, 0x1184, + 0x1190, 0x1194, + 0x11a0, 0x11a4, + 0x11b0, 0x11b4, 0x11fc, 0x123c, 0x1300, 0x173c, 0x1800, 0x18fc, - 0x3000, 0x305c, - 0x3068, 0x30d8, - 0x30e0, 0x5924, - 0x5960, 0x59d4, - 0x5a00, 0x5af8, + 0x3000, 0x30d8, + 0x30e0, 0x30e4, + 0x30ec, 0x5910, + 0x5920, 0x5924, + 0x5960, 0x5960, + 0x5968, 0x5968, + 0x5970, 0x5970, + 0x5978, 0x5978, + 0x5980, 0x5980, + 0x5988, 0x5988, + 0x5990, 0x5990, + 0x5998, 0x5998, + 0x59a0, 0x59d4, + 0x5a00, 0x5ae0, + 0x5ae8, 0x5ae8, + 0x5af0, 0x5af0, + 0x5af8, 0x5af8, 0x6000, 0x6098, 0x6100, 0x6150, 0x6200, 0x6208, 0x6240, 0x6248, - 0x6280, 0x6338, + 0x6280, 0x62b0, + 0x62c0, 0x6338, 0x6370, 0x638c, 0x6400, 0x643c, 0x6500, 0x6524, - 0x6a00, 0x6a38, - 0x6a60, 0x6a78, - 0x6b00, 0x6b84, - 0x6bf0, 0x6c84, - 0x6cf0, 0x6d84, - 0x6df0, 0x6e84, - 0x6ef0, 0x6f84, - 0x6ff0, 0x7084, - 0x70f0, 0x7184, - 0x71f0, 0x7284, - 0x72f0, 0x7384, - 0x73f0, 0x7450, + 0x6a00, 0x6a04, + 0x6a14, 0x6a38, + 0x6a60, 0x6a70, + 0x6a78, 0x6a78, + 0x6b00, 0x6b0c, + 0x6b1c, 0x6b84, + 0x6bf0, 0x6bf8, + 0x6c00, 0x6c0c, + 0x6c1c, 0x6c84, + 0x6cf0, 0x6cf8, + 0x6d00, 0x6d0c, + 0x6d1c, 0x6d84, + 0x6df0, 0x6df8, + 0x6e00, 0x6e0c, + 0x6e1c, 0x6e84, + 0x6ef0, 0x6ef8, + 0x6f00, 0x6f0c, + 0x6f1c, 0x6f84, + 0x6ff0, 0x6ff8, + 0x7000, 0x700c, + 0x701c, 0x7084, + 0x70f0, 0x70f8, + 0x7100, 0x710c, + 0x711c, 0x7184, + 0x71f0, 0x71f8, + 0x7200, 0x720c, + 0x721c, 0x7284, + 0x72f0, 0x72f8, + 0x7300, 0x730c, + 0x731c, 0x7384, + 0x73f0, 0x73f8, + 0x7400, 0x7450, 0x7500, 0x7530, - 0x7600, 0x761c, + 0x7600, 0x760c, + 0x7614, 0x761c, 0x7680, 0x76cc, 0x7700, 0x7798, 0x77c0, 0x77fc, 0x7900, 0x79fc, - 0x7b00, 0x7c38, - 0x7d00, 0x7efc, - 0x8dc0, 0x8e1c, + 0x7b00, 0x7b58, + 0x7b60, 0x7b84, + 0x7b8c, 0x7c38, + 0x7d00, 0x7d38, + 0x7d40, 0x7d80, + 0x7d8c, 0x7ddc, + 0x7de4, 0x7e04, + 0x7e10, 0x7e1c, + 0x7e24, 0x7e38, + 0x7e40, 0x7e44, + 0x7e4c, 0x7e78, + 0x7e80, 0x7ea4, + 0x7eac, 0x7edc, + 0x7ee8, 0x7efc, + 0x8dc0, 0x8e04, + 0x8e10, 0x8e1c, 0x8e30, 0x8e78, - 0x8ea0, 0x8f6c, - 0x8fc0, 0x9074, + 0x8ea0, 0x8eb8, + 0x8ec0, 0x8f6c, + 0x8fc0, 0x9008, + 0x9010, 0x9058, + 0x9060, 0x9060, + 0x9068, 0x9074, 0x90fc, 0x90fc, - 0x9400, 0x9458, - 0x9600, 0x96bc, + 0x9400, 0x9408, + 0x9410, 0x9458, + 0x9600, 0x9600, + 0x9608, 0x9638, + 0x9640, 0x96bc, 0x9800, 0x9808, 0x9820, 0x983c, 0x9850, 0x9864, @@ -754,23 +811,42 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x9e80, 0x9eec, 0x9f00, 0x9f6c, 0x9f80, 0x9fec, - 0xd004, 0xd03c, + 0xd004, 0xd004, + 0xd010, 0xd03c, 0xdfc0, 0xdfe0, 0xe000, 0xea7c, - 0xf000, 0x11110, - 0x11118, 0x11190, + 0xf000, 0x11190, 0x19040, 0x1906c, 0x19078, 0x19080, - 0x1908c, 0x19124, - 0x19150, 0x191b0, + 0x1908c, 0x190e4, + 0x190f0, 0x190f8, + 0x19100, 0x19110, + 0x19120, 0x19124, + 0x19150, 0x19194, + 0x1919c, 0x191b0, 0x191d0, 0x191e8, 0x19238, 0x1924c, - 0x193f8, 0x19474, - 0x19490, 0x194f8, - 0x19800, 0x19f4c, - 0x1a000, 0x1a06c, - 0x1a0b0, 0x1a120, - 0x1a128, 0x1a138, + 0x193f8, 0x1943c, + 0x1944c, 0x19474, + 0x19490, 0x194e0, + 0x194f0, 0x194f8, + 0x19800, 0x19c08, + 0x19c10, 0x19c90, + 0x19ca0, 0x19ce4, + 0x19cf0, 0x19d40, + 0x19d50, 0x19d94, + 0x19da0, 0x19de8, + 0x19df0, 0x19e40, + 0x19e50, 0x19e90, + 0x19ea0, 0x19f4c, + 0x1a000, 0x1a004, + 0x1a010, 0x1a06c, + 0x1a0b0, 0x1a0e4, + 0x1a0ec, 0x1a0f4, + 0x1a100, 0x1a108, + 0x1a114, 0x1a120, + 0x1a128, 0x1a130, + 0x1a138, 0x1a138, 0x1a190, 0x1a1c4, 0x1a1fc, 0x1a1fc, 0x1e040, 0x1e04c, @@ -823,9 +899,12 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x1ffc0, 0x1ffc8, 0x20000, 0x2002c, 0x20100, 0x2013c, - 0x20190, 0x201c8, + 0x20190, 0x201a0, + 0x201a8, 0x201b8, + 0x201c4, 0x201c8, 0x20200, 0x20318, - 0x20400, 0x20528, + 0x20400, 0x204b4, + 0x204c0, 0x20528, 0x20540, 0x20614, 0x21000, 0x21040, 0x2104c, 0x21060, @@ -834,22 +913,62 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x21270, 0x21284, 0x212fc, 0x21388, 0x21400, 0x21404, - 0x21500, 0x21518, - 0x2152c, 0x2153c, + 0x21500, 0x21500, + 0x21510, 0x21518, + 0x2152c, 0x21530, + 0x2153c, 0x2153c, 0x21550, 0x21554, 0x21600, 0x21600, - 0x21608, 0x21628, - 0x21630, 0x2163c, + 0x21608, 0x2161c, + 0x21624, 0x21628, + 0x21630, 0x21634, + 0x2163c, 0x2163c, 0x21700, 0x2171c, 0x21780, 0x2178c, - 0x21800, 0x21c38, - 0x21c80, 0x21d7c, + 0x21800, 0x21818, + 0x21820, 0x21828, + 0x21830, 0x21848, + 0x21850, 0x21854, + 0x21860, 0x21868, + 0x21870, 0x21870, + 0x21878, 0x21898, + 0x218a0, 0x218a8, + 0x218b0, 0x218c8, + 0x218d0, 0x218d4, + 0x218e0, 0x218e8, + 0x218f0, 0x218f0, + 0x218f8, 0x21a18, + 0x21a20, 0x21a28, + 0x21a30, 0x21a48, + 0x21a50, 0x21a54, + 0x21a60, 0x21a68, + 0x21a70, 0x21a70, + 0x21a78, 0x21a98, + 0x21aa0, 0x21aa8, + 0x21ab0, 0x21ac8, + 0x21ad0, 0x21ad4, + 0x21ae0, 0x21ae8, + 0x21af0, 0x21af0, + 0x21af8, 0x21c18, + 0x21c20, 0x21c20, + 0x21c28, 0x21c30, + 0x21c38, 0x21c38, + 0x21c80, 0x21c98, + 0x21ca0, 0x21ca8, + 0x21cb0, 0x21cc8, + 0x21cd0, 0x21cd4, + 0x21ce0, 0x21ce8, + 0x21cf0, 0x21cf0, + 0x21cf8, 0x21d7c, 0x21e00, 0x21e04, 0x22000, 0x2202c, 0x22100, 0x2213c, - 0x22190, 0x221c8, + 0x22190, 0x221a0, + 0x221a8, 0x221b8, + 0x221c4, 0x221c8, 0x22200, 0x22318, - 0x22400, 0x22528, + 0x22400, 0x224b4, + 0x224c0, 0x22528, 0x22540, 0x22614, 0x23000, 0x23040, 0x2304c, 0x23060, @@ -858,22 +977,62 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x23270, 0x23284, 0x232fc, 0x23388, 0x23400, 0x23404, - 0x23500, 0x23518, - 0x2352c, 0x2353c, + 0x23500, 0x23500, + 0x23510, 0x23518, + 0x2352c, 0x23530, + 0x2353c, 0x2353c, 0x23550, 0x23554, 0x23600, 0x23600, - 0x23608, 0x23628, - 0x23630, 0x2363c, + 0x23608, 0x2361c, + 0x23624, 0x23628, + 0x23630, 0x23634, + 0x2363c, 0x2363c, 0x23700, 0x2371c, 0x23780, 0x2378c, - 0x23800, 0x23c38, - 0x23c80, 0x23d7c, + 0x23800, 0x23818, + 0x23820, 0x23828, + 0x23830, 0x23848, + 0x23850, 0x23854, + 0x23860, 0x23868, + 0x23870, 0x23870, + 0x23878, 0x23898, + 0x238a0, 0x238a8, + 0x238b0, 0x238c8, + 0x238d0, 0x238d4, + 0x238e0, 0x238e8, + 0x238f0, 0x238f0, + 0x238f8, 0x23a18, + 0x23a20, 0x23a28, + 0x23a30, 0x23a48, + 0x23a50, 0x23a54, + 0x23a60, 0x23a68, + 0x23a70, 0x23a70, + 0x23a78, 0x23a98, + 0x23aa0, 0x23aa8, + 0x23ab0, 0x23ac8, + 0x23ad0, 0x23ad4, + 0x23ae0, 0x23ae8, + 0x23af0, 0x23af0, + 0x23af8, 0x23c18, + 0x23c20, 0x23c20, + 0x23c28, 0x23c30, + 0x23c38, 0x23c38, + 0x23c80, 0x23c98, + 0x23ca0, 0x23ca8, + 0x23cb0, 0x23cc8, + 0x23cd0, 0x23cd4, + 0x23ce0, 0x23ce8, + 0x23cf0, 0x23cf0, + 0x23cf8, 0x23d7c, 0x23e00, 0x23e04, 0x24000, 0x2402c, 0x24100, 0x2413c, - 0x24190, 0x241c8, + 0x24190, 0x241a0, + 0x241a8, 0x241b8, + 0x241c4, 0x241c8, 0x24200, 0x24318, - 0x24400, 0x24528, + 0x24400, 0x244b4, + 0x244c0, 0x24528, 0x24540, 0x24614, 0x25000, 0x25040, 0x2504c, 0x25060, @@ -882,22 +1041,62 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x25270, 0x25284, 0x252fc, 0x25388, 0x25400, 0x25404, - 0x25500, 0x25518, - 0x2552c, 0x2553c, + 0x25500, 0x25500, + 0x25510, 0x25518, + 0x2552c, 0x25530, + 0x2553c, 0x2553c, 0x25550, 0x25554, 0x25600, 0x25600, - 0x25608, 0x25628, - 0x25630, 0x2563c, + 0x25608, 0x2561c, + 0x25624, 0x25628, + 0x25630, 0x25634, + 0x2563c, 0x2563c, 0x25700, 0x2571c, 0x25780, 0x2578c, - 0x25800, 0x25c38, - 0x25c80, 0x25d7c, + 0x25800, 0x25818, + 0x25820, 0x25828, + 0x25830, 0x25848, + 0x25850, 0x25854, + 0x25860, 0x25868, + 0x25870, 0x25870, + 0x25878, 0x25898, + 0x258a0, 0x258a8, + 0x258b0, 0x258c8, + 0x258d0, 0x258d4, + 0x258e0, 0x258e8, + 0x258f0, 0x258f0, + 0x258f8, 0x25a18, + 0x25a20, 0x25a28, + 0x25a30, 0x25a48, + 0x25a50, 0x25a54, + 0x25a60, 0x25a68, + 0x25a70, 0x25a70, + 0x25a78, 0x25a98, + 0x25aa0, 0x25aa8, + 0x25ab0, 0x25ac8, + 0x25ad0, 0x25ad4, + 0x25ae0, 0x25ae8, + 0x25af0, 0x25af0, + 0x25af8, 0x25c18, + 0x25c20, 0x25c20, + 0x25c28, 0x25c30, + 0x25c38, 0x25c38, + 0x25c80, 0x25c98, + 0x25ca0, 0x25ca8, + 0x25cb0, 0x25cc8, + 0x25cd0, 0x25cd4, + 0x25ce0, 0x25ce8, + 0x25cf0, 0x25cf0, + 0x25cf8, 0x25d7c, 0x25e00, 0x25e04, 0x26000, 0x2602c, 0x26100, 0x2613c, - 0x26190, 0x261c8, + 0x26190, 0x261a0, + 0x261a8, 0x261b8, + 0x261c4, 0x261c8, 0x26200, 0x26318, - 0x26400, 0x26528, + 0x26400, 0x264b4, + 0x264c0, 0x26528, 0x26540, 0x26614, 0x27000, 0x27040, 0x2704c, 0x27060, @@ -906,51 +1105,120 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x27270, 0x27284, 0x272fc, 0x27388, 0x27400, 0x27404, - 0x27500, 0x27518, - 0x2752c, 0x2753c, + 0x27500, 0x27500, + 0x27510, 0x27518, + 0x2752c, 0x27530, + 0x2753c, 0x2753c, 0x27550, 0x27554, 0x27600, 0x27600, - 0x27608, 0x27628, - 0x27630, 0x2763c, + 0x27608, 0x2761c, + 0x27624, 0x27628, + 0x27630, 0x27634, + 0x2763c, 0x2763c, 0x27700, 0x2771c, 0x27780, 0x2778c, - 0x27800, 0x27c38, - 0x27c80, 0x27d7c, + 0x27800, 0x27818, + 0x27820, 0x27828, + 0x27830, 0x27848, + 0x27850, 0x27854, + 0x27860, 0x27868, + 0x27870, 0x27870, + 0x27878, 0x27898, + 0x278a0, 0x278a8, + 0x278b0, 0x278c8, + 0x278d0, 0x278d4, + 0x278e0, 0x278e8, + 0x278f0, 0x278f0, + 0x278f8, 0x27a18, + 0x27a20, 0x27a28, + 0x27a30, 0x27a48, + 0x27a50, 0x27a54, + 0x27a60, 0x27a68, + 0x27a70, 0x27a70, + 0x27a78, 0x27a98, + 0x27aa0, 0x27aa8, + 0x27ab0, 0x27ac8, + 0x27ad0, 0x27ad4, + 0x27ae0, 0x27ae8, + 0x27af0, 0x27af0, + 0x27af8, 0x27c18, + 0x27c20, 0x27c20, + 0x27c28, 0x27c30, + 0x27c38, 0x27c38, + 0x27c80, 0x27c98, + 0x27ca0, 0x27ca8, + 0x27cb0, 0x27cc8, + 0x27cd0, 0x27cd4, + 0x27ce0, 0x27ce8, + 0x27cf0, 0x27cf0, + 0x27cf8, 0x27d7c, 0x27e00, 0x27e04, }; static const unsigned int t5_reg_ranges[] = { - 0x1008, 0x1148, - 0x1180, 0x11b4, + 0x1008, 0x10c0, + 0x10cc, 0x10f8, + 0x1100, 0x1100, + 0x110c, 0x1148, + 0x1180, 0x1184, + 0x1190, 0x1194, + 0x11a0, 0x11a4, + 0x11b0, 0x11b4, 0x11fc, 0x123c, 0x1280, 0x173c, 0x1800, 0x18fc, 0x3000, 0x3028, - 0x3068, 0x30d8, + 0x3060, 0x30b0, + 0x30b8, 0x30d8, 0x30e0, 0x30fc, 0x3140, 0x357c, 0x35a8, 0x35cc, 0x35ec, 0x35ec, 0x3600, 0x5624, - 0x56cc, 0x575c, + 0x56cc, 0x56ec, + 0x56f4, 0x5720, + 0x5728, 0x575c, 0x580c, 0x5814, - 0x5890, 0x58bc, - 0x5940, 0x59dc, + 0x5890, 0x589c, + 0x58a4, 0x58ac, + 0x58b8, 0x58bc, + 0x5940, 0x59c8, + 0x59d0, 0x59dc, 0x59fc, 0x5a18, - 0x5a60, 0x5a9c, + 0x5a60, 0x5a70, + 0x5a80, 0x5a9c, 0x5b94, 0x5bfc, - 0x6000, 0x6040, - 0x6058, 0x614c, + 0x6000, 0x6020, + 0x6028, 0x6040, + 0x6058, 0x609c, + 0x60a8, 0x614c, 0x7700, 0x7798, 0x77c0, 0x78fc, - 0x7b00, 0x7c54, - 0x7d00, 0x7efc, + 0x7b00, 0x7b58, + 0x7b60, 0x7b84, + 0x7b8c, 0x7c54, + 0x7d00, 0x7d38, + 0x7d40, 0x7d80, + 0x7d8c, 0x7ddc, + 0x7de4, 0x7e04, + 0x7e10, 0x7e1c, + 0x7e24, 0x7e38, + 0x7e40, 0x7e44, + 0x7e4c, 0x7e78, + 0x7e80, 0x7edc, + 0x7ee8, 0x7efc, 0x8dc0, 0x8de0, - 0x8df8, 0x8e84, + 0x8df8, 0x8e04, + 0x8e10, 0x8e84, 0x8ea0, 0x8f84, - 0x8fc0, 0x90f8, - 0x9400, 0x9470, - 0x9600, 0x96f4, + 0x8fc0, 0x9058, + 0x9060, 0x9060, + 0x9068, 0x90f8, + 0x9400, 0x9408, + 0x9410, 0x9470, + 0x9600, 0x9600, + 0x9608, 0x9638, + 0x9640, 0x96f4, 0x9800, 0x9808, 0x9820, 0x983c, 0x9850, 0x9864, @@ -962,103 +1230,143 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x9e80, 0x9eec, 0x9f00, 0x9f6c, 0x9f80, 0xa020, - 0xd004, 0xd03c, + 0xd004, 0xd004, + 0xd010, 0xd03c, 0xdfc0, 0xdfe0, - 0xe000, 0x11088, - 0x1109c, 0x11110, - 0x11118, 0x1117c, + 0xe000, 0x1106c, + 0x11074, 0x11088, + 0x1109c, 0x1117c, 0x11190, 0x11204, 0x19040, 0x1906c, 0x19078, 0x19080, - 0x1908c, 0x19124, - 0x19150, 0x191b0, + 0x1908c, 0x190e8, + 0x190f0, 0x190f8, + 0x19100, 0x19110, + 0x19120, 0x19124, + 0x19150, 0x19194, + 0x1919c, 0x191b0, 0x191d0, 0x191e8, 0x19238, 0x19290, - 0x193f8, 0x19474, + 0x193f8, 0x19428, + 0x19430, 0x19444, + 0x1944c, 0x1946c, + 0x19474, 0x19474, 0x19490, 0x194cc, 0x194f0, 0x194f8, - 0x19c00, 0x19c60, - 0x19c94, 0x19e10, - 0x19e50, 0x19f34, + 0x19c00, 0x19c08, + 0x19c10, 0x19c60, + 0x19c94, 0x19ce4, + 0x19cf0, 0x19d40, + 0x19d50, 0x19d94, + 0x19da0, 0x19de8, + 0x19df0, 0x19e10, + 0x19e50, 0x19e90, + 0x19ea0, 0x19f24, + 0x19f34, 0x19f34, 0x19f40, 0x19f50, - 0x19f90, 0x19fe4, - 0x1a000, 0x1a06c, - 0x1a0b0, 0x1a120, - 0x1a128, 0x1a138, + 0x19f90, 0x19fb4, + 0x19fc4, 0x19fe4, + 0x1a000, 0x1a004, + 0x1a010, 0x1a06c, + 0x1a0b0, 0x1a0e4, + 0x1a0ec, 0x1a0f8, + 0x1a100, 0x1a108, + 0x1a114, 0x1a120, + 0x1a128, 0x1a130, + 0x1a138, 0x1a138, 0x1a190, 0x1a1c4, 0x1a1fc, 0x1a1fc, 0x1e008, 0x1e00c, - 0x1e040, 0x1e04c, + 0x1e040, 0x1e044, + 0x1e04c, 0x1e04c, 0x1e284, 0x1e290, 0x1e2c0, 0x1e2c0, 0x1e2e0, 0x1e2e0, 0x1e300, 0x1e384, 0x1e3c0, 0x1e3c8, 0x1e408, 0x1e40c, - 0x1e440, 0x1e44c, + 0x1e440, 0x1e444, + 0x1e44c, 0x1e44c, 0x1e684, 0x1e690, 0x1e6c0, 0x1e6c0, 0x1e6e0, 0x1e6e0, 0x1e700, 0x1e784, 0x1e7c0, 0x1e7c8, 0x1e808, 0x1e80c, - 0x1e840, 0x1e84c, + 0x1e840, 0x1e844, + 0x1e84c, 0x1e84c, 0x1ea84, 0x1ea90, 0x1eac0, 0x1eac0, 0x1eae0, 0x1eae0, 0x1eb00, 0x1eb84, 0x1ebc0, 0x1ebc8, 0x1ec08, 0x1ec0c, - 0x1ec40, 0x1ec4c, + 0x1ec40, 0x1ec44, + 0x1ec4c, 0x1ec4c, 0x1ee84, 0x1ee90, 0x1eec0, 0x1eec0, 0x1eee0, 0x1eee0, 0x1ef00, 0x1ef84, 0x1efc0, 0x1efc8, 0x1f008, 0x1f00c, - 0x1f040, 0x1f04c, + 0x1f040, 0x1f044, + 0x1f04c, 0x1f04c, 0x1f284, 0x1f290, 0x1f2c0, 0x1f2c0, 0x1f2e0, 0x1f2e0, 0x1f300, 0x1f384, 0x1f3c0, 0x1f3c8, 0x1f408, 0x1f40c, - 0x1f440, 0x1f44c, + 0x1f440, 0x1f444, + 0x1f44c, 0x1f44c, 0x1f684, 0x1f690, 0x1f6c0, 0x1f6c0, 0x1f6e0, 0x1f6e0, 0x1f700, 0x1f784, 0x1f7c0, 0x1f7c8, 0x1f808, 0x1f80c, - 0x1f840, 0x1f84c, + 0x1f840, 0x1f844, + 0x1f84c, 0x1f84c, 0x1fa84, 0x1fa90, 0x1fac0, 0x1fac0, 0x1fae0, 0x1fae0, 0x1fb00, 0x1fb84, 0x1fbc0, 0x1fbc8, 0x1fc08, 0x1fc0c, - 0x1fc40, 0x1fc4c, + 0x1fc40, 0x1fc44, + 0x1fc4c, 0x1fc4c, 0x1fe84, 0x1fe90, 0x1fec0, 0x1fec0, 0x1fee0, 0x1fee0, 0x1ff00, 0x1ff84, 0x1ffc0, 0x1ffc8, 0x30000, 0x30030, + 0x30038, 0x30038, + 0x30040, 0x30040, 0x30100, 0x30144, - 0x30190, 0x301d0, + 0x30190, 0x301a0, + 0x301a8, 0x301b8, + 0x301c4, 0x301c8, + 0x301d0, 0x301d0, 0x30200, 0x30318, - 0x30400, 0x3052c, + 0x30400, 0x304b4, + 0x304c0, 0x3052c, 0x30540, 0x3061c, - 0x30800, 0x30834, + 0x30800, 0x30828, + 0x30834, 0x30834, 0x308c0, 0x30908, 0x30910, 0x309ac, - 0x30a00, 0x30a2c, + 0x30a00, 0x30a14, + 0x30a1c, 0x30a2c, 0x30a44, 0x30a50, - 0x30a74, 0x30c24, + 0x30a74, 0x30a74, + 0x30a7c, 0x30afc, + 0x30b08, 0x30c24, 0x30d00, 0x30d00, 0x30d08, 0x30d14, 0x30d1c, 0x30d20, - 0x30d3c, 0x30d50, + 0x30d3c, 0x30d3c, + 0x30d48, 0x30d50, 0x31200, 0x3120c, 0x31220, 0x31220, 0x31240, 0x31240, @@ -1078,27 +1386,65 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x322c8, 0x322fc, 0x32600, 0x32630, 0x32a00, 0x32abc, - 0x32b00, 0x32b70, - 0x33000, 0x33048, - 0x33060, 0x3309c, - 0x330f0, 0x33148, - 0x33160, 0x3319c, - 0x331f0, 0x332e4, - 0x332f8, 0x333e4, - 0x333f8, 0x33448, - 0x33460, 0x3349c, - 0x334f0, 0x33548, - 0x33560, 0x3359c, - 0x335f0, 0x336e4, - 0x336f8, 0x337e4, + 0x32b00, 0x32b10, + 0x32b20, 0x32b30, + 0x32b40, 0x32b50, + 0x32b60, 0x32b70, + 0x33000, 0x33028, + 0x33030, 0x33048, + 0x33060, 0x33068, + 0x33070, 0x3309c, + 0x330f0, 0x33128, + 0x33130, 0x33148, + 0x33160, 0x33168, + 0x33170, 0x3319c, + 0x331f0, 0x33238, + 0x33240, 0x33240, + 0x33248, 0x33250, + 0x3325c, 0x33264, + 0x33270, 0x332b8, + 0x332c0, 0x332e4, + 0x332f8, 0x33338, + 0x33340, 0x33340, + 0x33348, 0x33350, + 0x3335c, 0x33364, + 0x33370, 0x333b8, + 0x333c0, 0x333e4, + 0x333f8, 0x33428, + 0x33430, 0x33448, + 0x33460, 0x33468, + 0x33470, 0x3349c, + 0x334f0, 0x33528, + 0x33530, 0x33548, + 0x33560, 0x33568, + 0x33570, 0x3359c, + 0x335f0, 0x33638, + 0x33640, 0x33640, + 0x33648, 0x33650, + 0x3365c, 0x33664, + 0x33670, 0x336b8, + 0x336c0, 0x336e4, + 0x336f8, 0x33738, + 0x33740, 0x33740, + 0x33748, 0x33750, + 0x3375c, 0x33764, + 0x33770, 0x337b8, + 0x337c0, 0x337e4, 0x337f8, 0x337fc, 0x33814, 0x33814, 0x3382c, 0x3382c, 0x33880, 0x3388c, 0x338e8, 0x338ec, - 0x33900, 0x33948, - 0x33960, 0x3399c, - 0x339f0, 0x33ae4, + 0x33900, 0x33928, + 0x33930, 0x33948, + 0x33960, 0x33968, + 0x33970, 0x3399c, + 0x339f0, 0x33a38, + 0x33a40, 0x33a40, + 0x33a48, 0x33a50, + 0x33a5c, 0x33a64, + 0x33a70, 0x33ab8, + 0x33ac0, 0x33ae4, 0x33af8, 0x33b10, 0x33b28, 0x33b28, 0x33b3c, 0x33b50, @@ -1107,21 +1453,32 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x33c3c, 0x33c50, 0x33cf0, 0x33cfc, 0x34000, 0x34030, + 0x34038, 0x34038, + 0x34040, 0x34040, 0x34100, 0x34144, - 0x34190, 0x341d0, + 0x34190, 0x341a0, + 0x341a8, 0x341b8, + 0x341c4, 0x341c8, + 0x341d0, 0x341d0, 0x34200, 0x34318, - 0x34400, 0x3452c, + 0x34400, 0x344b4, + 0x344c0, 0x3452c, 0x34540, 0x3461c, - 0x34800, 0x34834, + 0x34800, 0x34828, + 0x34834, 0x34834, 0x348c0, 0x34908, 0x34910, 0x349ac, - 0x34a00, 0x34a2c, + 0x34a00, 0x34a14, + 0x34a1c, 0x34a2c, 0x34a44, 0x34a50, - 0x34a74, 0x34c24, + 0x34a74, 0x34a74, + 0x34a7c, 0x34afc, + 0x34b08, 0x34c24, 0x34d00, 0x34d00, 0x34d08, 0x34d14, 0x34d1c, 0x34d20, - 0x34d3c, 0x34d50, + 0x34d3c, 0x34d3c, + 0x34d48, 0x34d50, 0x35200, 0x3520c, 0x35220, 0x35220, 0x35240, 0x35240, @@ -1141,27 +1498,65 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x362c8, 0x362fc, 0x36600, 0x36630, 0x36a00, 0x36abc, - 0x36b00, 0x36b70, - 0x37000, 0x37048, - 0x37060, 0x3709c, - 0x370f0, 0x37148, - 0x37160, 0x3719c, - 0x371f0, 0x372e4, - 0x372f8, 0x373e4, - 0x373f8, 0x37448, - 0x37460, 0x3749c, - 0x374f0, 0x37548, - 0x37560, 0x3759c, - 0x375f0, 0x376e4, - 0x376f8, 0x377e4, + 0x36b00, 0x36b10, + 0x36b20, 0x36b30, + 0x36b40, 0x36b50, + 0x36b60, 0x36b70, + 0x37000, 0x37028, + 0x37030, 0x37048, + 0x37060, 0x37068, + 0x37070, 0x3709c, + 0x370f0, 0x37128, + 0x37130, 0x37148, + 0x37160, 0x37168, + 0x37170, 0x3719c, + 0x371f0, 0x37238, + 0x37240, 0x37240, + 0x37248, 0x37250, + 0x3725c, 0x37264, + 0x37270, 0x372b8, + 0x372c0, 0x372e4, + 0x372f8, 0x37338, + 0x37340, 0x37340, + 0x37348, 0x37350, + 0x3735c, 0x37364, + 0x37370, 0x373b8, + 0x373c0, 0x373e4, + 0x373f8, 0x37428, + 0x37430, 0x37448, + 0x37460, 0x37468, + 0x37470, 0x3749c, + 0x374f0, 0x37528, + 0x37530, 0x37548, + 0x37560, 0x37568, + 0x37570, 0x3759c, + 0x375f0, 0x37638, + 0x37640, 0x37640, + 0x37648, 0x37650, + 0x3765c, 0x37664, + 0x37670, 0x376b8, + 0x376c0, 0x376e4, + 0x376f8, 0x37738, + 0x37740, 0x37740, + 0x37748, 0x37750, + 0x3775c, 0x37764, + 0x37770, 0x377b8, + 0x377c0, 0x377e4, 0x377f8, 0x377fc, 0x37814, 0x37814, 0x3782c, 0x3782c, 0x37880, 0x3788c, 0x378e8, 0x378ec, - 0x37900, 0x37948, - 0x37960, 0x3799c, - 0x379f0, 0x37ae4, + 0x37900, 0x37928, + 0x37930, 0x37948, + 0x37960, 0x37968, + 0x37970, 0x3799c, + 0x379f0, 0x37a38, + 0x37a40, 0x37a40, + 0x37a48, 0x37a50, + 0x37a5c, 0x37a64, + 0x37a70, 0x37ab8, + 0x37ac0, 0x37ae4, 0x37af8, 0x37b10, 0x37b28, 0x37b28, 0x37b3c, 0x37b50, @@ -1170,21 +1565,32 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x37c3c, 0x37c50, 0x37cf0, 0x37cfc, 0x38000, 0x38030, + 0x38038, 0x38038, + 0x38040, 0x38040, 0x38100, 0x38144, - 0x38190, 0x381d0, + 0x38190, 0x381a0, + 0x381a8, 0x381b8, + 0x381c4, 0x381c8, + 0x381d0, 0x381d0, 0x38200, 0x38318, - 0x38400, 0x3852c, + 0x38400, 0x384b4, + 0x384c0, 0x3852c, 0x38540, 0x3861c, - 0x38800, 0x38834, + 0x38800, 0x38828, + 0x38834, 0x38834, 0x388c0, 0x38908, 0x38910, 0x389ac, - 0x38a00, 0x38a2c, + 0x38a00, 0x38a14, + 0x38a1c, 0x38a2c, 0x38a44, 0x38a50, - 0x38a74, 0x38c24, + 0x38a74, 0x38a74, + 0x38a7c, 0x38afc, + 0x38b08, 0x38c24, 0x38d00, 0x38d00, 0x38d08, 0x38d14, 0x38d1c, 0x38d20, - 0x38d3c, 0x38d50, + 0x38d3c, 0x38d3c, + 0x38d48, 0x38d50, 0x39200, 0x3920c, 0x39220, 0x39220, 0x39240, 0x39240, @@ -1204,27 +1610,65 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x3a2c8, 0x3a2fc, 0x3a600, 0x3a630, 0x3aa00, 0x3aabc, - 0x3ab00, 0x3ab70, - 0x3b000, 0x3b048, - 0x3b060, 0x3b09c, - 0x3b0f0, 0x3b148, - 0x3b160, 0x3b19c, - 0x3b1f0, 0x3b2e4, - 0x3b2f8, 0x3b3e4, - 0x3b3f8, 0x3b448, - 0x3b460, 0x3b49c, - 0x3b4f0, 0x3b548, - 0x3b560, 0x3b59c, - 0x3b5f0, 0x3b6e4, - 0x3b6f8, 0x3b7e4, + 0x3ab00, 0x3ab10, + 0x3ab20, 0x3ab30, + 0x3ab40, 0x3ab50, + 0x3ab60, 0x3ab70, + 0x3b000, 0x3b028, + 0x3b030, 0x3b048, + 0x3b060, 0x3b068, + 0x3b070, 0x3b09c, + 0x3b0f0, 0x3b128, + 0x3b130, 0x3b148, + 0x3b160, 0x3b168, + 0x3b170, 0x3b19c, + 0x3b1f0, 0x3b238, + 0x3b240, 0x3b240, + 0x3b248, 0x3b250, + 0x3b25c, 0x3b264, + 0x3b270, 0x3b2b8, + 0x3b2c0, 0x3b2e4, + 0x3b2f8, 0x3b338, + 0x3b340, 0x3b340, + 0x3b348, 0x3b350, + 0x3b35c, 0x3b364, + 0x3b370, 0x3b3b8, + 0x3b3c0, 0x3b3e4, + 0x3b3f8, 0x3b428, + 0x3b430, 0x3b448, + 0x3b460, 0x3b468, + 0x3b470, 0x3b49c, + 0x3b4f0, 0x3b528, + 0x3b530, 0x3b548, + 0x3b560, 0x3b568, + 0x3b570, 0x3b59c, + 0x3b5f0, 0x3b638, + 0x3b640, 0x3b640, + 0x3b648, 0x3b650, + 0x3b65c, 0x3b664, + 0x3b670, 0x3b6b8, + 0x3b6c0, 0x3b6e4, + 0x3b6f8, 0x3b738, + 0x3b740, 0x3b740, + 0x3b748, 0x3b750, + 0x3b75c, 0x3b764, + 0x3b770, 0x3b7b8, + 0x3b7c0, 0x3b7e4, 0x3b7f8, 0x3b7fc, 0x3b814, 0x3b814, 0x3b82c, 0x3b82c, 0x3b880, 0x3b88c, 0x3b8e8, 0x3b8ec, - 0x3b900, 0x3b948, - 0x3b960, 0x3b99c, - 0x3b9f0, 0x3bae4, + 0x3b900, 0x3b928, + 0x3b930, 0x3b948, + 0x3b960, 0x3b968, + 0x3b970, 0x3b99c, + 0x3b9f0, 0x3ba38, + 0x3ba40, 0x3ba40, + 0x3ba48, 0x3ba50, + 0x3ba5c, 0x3ba64, + 0x3ba70, 0x3bab8, + 0x3bac0, 0x3bae4, 0x3baf8, 0x3bb10, 0x3bb28, 0x3bb28, 0x3bb3c, 0x3bb50, @@ -1233,21 +1677,32 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x3bc3c, 0x3bc50, 0x3bcf0, 0x3bcfc, 0x3c000, 0x3c030, + 0x3c038, 0x3c038, + 0x3c040, 0x3c040, 0x3c100, 0x3c144, - 0x3c190, 0x3c1d0, + 0x3c190, 0x3c1a0, + 0x3c1a8, 0x3c1b8, + 0x3c1c4, 0x3c1c8, + 0x3c1d0, 0x3c1d0, 0x3c200, 0x3c318, - 0x3c400, 0x3c52c, + 0x3c400, 0x3c4b4, + 0x3c4c0, 0x3c52c, 0x3c540, 0x3c61c, - 0x3c800, 0x3c834, + 0x3c800, 0x3c828, + 0x3c834, 0x3c834, 0x3c8c0, 0x3c908, 0x3c910, 0x3c9ac, - 0x3ca00, 0x3ca2c, + 0x3ca00, 0x3ca14, + 0x3ca1c, 0x3ca2c, 0x3ca44, 0x3ca50, - 0x3ca74, 0x3cc24, + 0x3ca74, 0x3ca74, + 0x3ca7c, 0x3cafc, + 0x3cb08, 0x3cc24, 0x3cd00, 0x3cd00, 0x3cd08, 0x3cd14, 0x3cd1c, 0x3cd20, - 0x3cd3c, 0x3cd50, + 0x3cd3c, 0x3cd3c, + 0x3cd48, 0x3cd50, 0x3d200, 0x3d20c, 0x3d220, 0x3d220, 0x3d240, 0x3d240, @@ -1267,27 +1722,65 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x3e2c8, 0x3e2fc, 0x3e600, 0x3e630, 0x3ea00, 0x3eabc, - 0x3eb00, 0x3eb70, - 0x3f000, 0x3f048, - 0x3f060, 0x3f09c, - 0x3f0f0, 0x3f148, - 0x3f160, 0x3f19c, - 0x3f1f0, 0x3f2e4, - 0x3f2f8, 0x3f3e4, - 0x3f3f8, 0x3f448, - 0x3f460, 0x3f49c, - 0x3f4f0, 0x3f548, - 0x3f560, 0x3f59c, - 0x3f5f0, 0x3f6e4, - 0x3f6f8, 0x3f7e4, + 0x3eb00, 0x3eb10, + 0x3eb20, 0x3eb30, + 0x3eb40, 0x3eb50, + 0x3eb60, 0x3eb70, + 0x3f000, 0x3f028, + 0x3f030, 0x3f048, + 0x3f060, 0x3f068, + 0x3f070, 0x3f09c, + 0x3f0f0, 0x3f128, + 0x3f130, 0x3f148, + 0x3f160, 0x3f168, + 0x3f170, 0x3f19c, + 0x3f1f0, 0x3f238, + 0x3f240, 0x3f240, + 0x3f248, 0x3f250, + 0x3f25c, 0x3f264, + 0x3f270, 0x3f2b8, + 0x3f2c0, 0x3f2e4, + 0x3f2f8, 0x3f338, + 0x3f340, 0x3f340, + 0x3f348, 0x3f350, + 0x3f35c, 0x3f364, + 0x3f370, 0x3f3b8, + 0x3f3c0, 0x3f3e4, + 0x3f3f8, 0x3f428, + 0x3f430, 0x3f448, + 0x3f460, 0x3f468, + 0x3f470, 0x3f49c, + 0x3f4f0, 0x3f528, + 0x3f530, 0x3f548, + 0x3f560, 0x3f568, + 0x3f570, 0x3f59c, + 0x3f5f0, 0x3f638, + 0x3f640, 0x3f640, + 0x3f648, 0x3f650, + 0x3f65c, 0x3f664, + 0x3f670, 0x3f6b8, + 0x3f6c0, 0x3f6e4, + 0x3f6f8, 0x3f738, + 0x3f740, 0x3f740, + 0x3f748, 0x3f750, + 0x3f75c, 0x3f764, + 0x3f770, 0x3f7b8, + 0x3f7c0, 0x3f7e4, 0x3f7f8, 0x3f7fc, 0x3f814, 0x3f814, 0x3f82c, 0x3f82c, 0x3f880, 0x3f88c, 0x3f8e8, 0x3f8ec, - 0x3f900, 0x3f948, - 0x3f960, 0x3f99c, - 0x3f9f0, 0x3fae4, + 0x3f900, 0x3f928, + 0x3f930, 0x3f948, + 0x3f960, 0x3f968, + 0x3f970, 0x3f99c, + 0x3f9f0, 0x3fa38, + 0x3fa40, 0x3fa40, + 0x3fa48, 0x3fa50, + 0x3fa5c, 0x3fa64, + 0x3fa70, 0x3fab8, + 0x3fac0, 0x3fae4, 0x3faf8, 0x3fb10, 0x3fb28, 0x3fb28, 0x3fb3c, 0x3fb50, @@ -1296,108 +1789,224 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x3fc3c, 0x3fc50, 0x3fcf0, 0x3fcfc, 0x40000, 0x4000c, - 0x40040, 0x40068, - 0x4007c, 0x40144, + 0x40040, 0x40050, + 0x40060, 0x40068, + 0x4007c, 0x4008c, + 0x40094, 0x400b0, + 0x400c0, 0x40144, 0x40180, 0x4018c, - 0x40200, 0x40298, - 0x402ac, 0x4033c, + 0x40200, 0x40254, + 0x40260, 0x40264, + 0x40270, 0x40288, + 0x40290, 0x40298, + 0x402ac, 0x402c8, + 0x402d0, 0x402e0, + 0x402f0, 0x402f0, + 0x40300, 0x4033c, 0x403f8, 0x403fc, 0x41304, 0x413c4, - 0x41400, 0x4141c, + 0x41400, 0x4140c, + 0x41414, 0x4141c, 0x41480, 0x414d0, - 0x44000, 0x44078, - 0x440c0, 0x44278, - 0x442c0, 0x44478, - 0x444c0, 0x44678, - 0x446c0, 0x44878, - 0x448c0, 0x449fc, - 0x45000, 0x45068, + 0x44000, 0x44054, + 0x4405c, 0x44078, + 0x440c0, 0x44174, + 0x44180, 0x441ac, + 0x441b4, 0x441b8, + 0x441c0, 0x44254, + 0x4425c, 0x44278, + 0x442c0, 0x44374, + 0x44380, 0x443ac, + 0x443b4, 0x443b8, + 0x443c0, 0x44454, + 0x4445c, 0x44478, + 0x444c0, 0x44574, + 0x44580, 0x445ac, + 0x445b4, 0x445b8, + 0x445c0, 0x44654, + 0x4465c, 0x44678, + 0x446c0, 0x44774, + 0x44780, 0x447ac, + 0x447b4, 0x447b8, + 0x447c0, 0x44854, + 0x4485c, 0x44878, + 0x448c0, 0x44974, + 0x44980, 0x449ac, + 0x449b4, 0x449b8, + 0x449c0, 0x449fc, + 0x45000, 0x45004, + 0x45010, 0x45030, + 0x45040, 0x45060, + 0x45068, 0x45068, 0x45080, 0x45084, 0x450a0, 0x450b0, - 0x45200, 0x45268, + 0x45200, 0x45204, + 0x45210, 0x45230, + 0x45240, 0x45260, + 0x45268, 0x45268, 0x45280, 0x45284, 0x452a0, 0x452b0, 0x460c0, 0x460e4, - 0x47000, 0x4708c, + 0x47000, 0x4703c, + 0x47044, 0x4708c, 0x47200, 0x47250, - 0x47400, 0x47420, + 0x47400, 0x47408, + 0x47414, 0x47420, 0x47600, 0x47618, 0x47800, 0x47814, 0x48000, 0x4800c, - 0x48040, 0x48068, - 0x4807c, 0x48144, + 0x48040, 0x48050, + 0x48060, 0x48068, + 0x4807c, 0x4808c, + 0x48094, 0x480b0, + 0x480c0, 0x48144, 0x48180, 0x4818c, - 0x48200, 0x48298, - 0x482ac, 0x4833c, + 0x48200, 0x48254, + 0x48260, 0x48264, + 0x48270, 0x48288, + 0x48290, 0x48298, + 0x482ac, 0x482c8, + 0x482d0, 0x482e0, + 0x482f0, 0x482f0, + 0x48300, 0x4833c, 0x483f8, 0x483fc, 0x49304, 0x493c4, - 0x49400, 0x4941c, + 0x49400, 0x4940c, + 0x49414, 0x4941c, 0x49480, 0x494d0, - 0x4c000, 0x4c078, - 0x4c0c0, 0x4c278, - 0x4c2c0, 0x4c478, - 0x4c4c0, 0x4c678, - 0x4c6c0, 0x4c878, - 0x4c8c0, 0x4c9fc, - 0x4d000, 0x4d068, + 0x4c000, 0x4c054, + 0x4c05c, 0x4c078, + 0x4c0c0, 0x4c174, + 0x4c180, 0x4c1ac, + 0x4c1b4, 0x4c1b8, + 0x4c1c0, 0x4c254, + 0x4c25c, 0x4c278, + 0x4c2c0, 0x4c374, + 0x4c380, 0x4c3ac, + 0x4c3b4, 0x4c3b8, + 0x4c3c0, 0x4c454, + 0x4c45c, 0x4c478, + 0x4c4c0, 0x4c574, + 0x4c580, 0x4c5ac, + 0x4c5b4, 0x4c5b8, + 0x4c5c0, 0x4c654, + 0x4c65c, 0x4c678, + 0x4c6c0, 0x4c774, + 0x4c780, 0x4c7ac, + 0x4c7b4, 0x4c7b8, + 0x4c7c0, 0x4c854, + 0x4c85c, 0x4c878, + 0x4c8c0, 0x4c974, + 0x4c980, 0x4c9ac, + 0x4c9b4, 0x4c9b8, + 0x4c9c0, 0x4c9fc, + 0x4d000, 0x4d004, + 0x4d010, 0x4d030, + 0x4d040, 0x4d060, + 0x4d068, 0x4d068, 0x4d080, 0x4d084, 0x4d0a0, 0x4d0b0, - 0x4d200, 0x4d268, + 0x4d200, 0x4d204, + 0x4d210, 0x4d230, + 0x4d240, 0x4d260, + 0x4d268, 0x4d268, 0x4d280, 0x4d284, 0x4d2a0, 0x4d2b0, 0x4e0c0, 0x4e0e4, - 0x4f000, 0x4f08c, + 0x4f000, 0x4f03c, + 0x4f044, 0x4f08c, 0x4f200, 0x4f250, - 0x4f400, 0x4f420, + 0x4f400, 0x4f408, + 0x4f414, 0x4f420, 0x4f600, 0x4f618, 0x4f800, 0x4f814, - 0x50000, 0x500cc, + 0x50000, 0x50084, + 0x50090, 0x500cc, 0x50400, 0x50400, - 0x50800, 0x508cc, + 0x50800, 0x50884, + 0x50890, 0x508cc, 0x50c00, 0x50c00, 0x51000, 0x5101c, 0x51300, 0x51308, }; static const unsigned int t6_reg_ranges[] = { - 0x1008, 0x1124, - 0x1138, 0x114c, - 0x1180, 0x11b4, + 0x1008, 0x101c, + 0x1024, 0x10a8, + 0x10b4, 0x10f8, + 0x1100, 0x1114, + 0x111c, 0x112c, + 0x1138, 0x113c, + 0x1144, 0x114c, + 0x1180, 0x1184, + 0x1190, 0x1194, + 0x11a0, 0x11a4, + 0x11b0, 0x11b4, 0x11fc, 0x1254, 0x1280, 0x133c, 0x1800, 0x18fc, 0x3000, 0x302c, - 0x3060, 0x30d8, + 0x3060, 0x30b0, + 0x30b8, 0x30d8, 0x30e0, 0x30fc, 0x3140, 0x357c, 0x35a8, 0x35cc, 0x35ec, 0x35ec, 0x3600, 0x5624, - 0x56cc, 0x575c, + 0x56cc, 0x56ec, + 0x56f4, 0x5720, + 0x5728, 0x575c, 0x580c, 0x5814, - 0x5890, 0x58bc, + 0x5890, 0x589c, + 0x58a4, 0x58ac, + 0x58b8, 0x58bc, 0x5940, 0x595c, 0x5980, 0x598c, - 0x59b0, 0x59dc, + 0x59b0, 0x59c8, + 0x59d0, 0x59dc, 0x59fc, 0x5a18, 0x5a60, 0x5a6c, - 0x5a80, 0x5a9c, + 0x5a80, 0x5a8c, + 0x5a94, 0x5a9c, 0x5b94, 0x5bfc, - 0x5c10, 0x5ec0, + 0x5c10, 0x5e48, + 0x5e50, 0x5e94, + 0x5ea0, 0x5eb0, + 0x5ec0, 0x5ec0, 0x5ec8, 0x5ecc, - 0x6000, 0x6040, - 0x6058, 0x619c, + 0x6000, 0x6020, + 0x6028, 0x6040, + 0x6058, 0x609c, + 0x60a8, 0x619c, 0x7700, 0x7798, 0x77c0, 0x7880, 0x78cc, 0x78fc, - 0x7b00, 0x7c54, - 0x7d00, 0x7efc, + 0x7b00, 0x7b58, + 0x7b60, 0x7b84, + 0x7b8c, 0x7c54, + 0x7d00, 0x7d38, + 0x7d40, 0x7d84, + 0x7d8c, 0x7ddc, + 0x7de4, 0x7e04, + 0x7e10, 0x7e1c, + 0x7e24, 0x7e38, + 0x7e40, 0x7e44, + 0x7e4c, 0x7e78, + 0x7e80, 0x7edc, + 0x7ee8, 0x7efc, 0x8dc0, 0x8de4, - 0x8df8, 0x8e84, + 0x8df8, 0x8e04, + 0x8e10, 0x8e84, 0x8ea0, 0x8f88, - 0x8fb8, 0x9124, + 0x8fb8, 0x9058, + 0x9060, 0x9060, + 0x9068, 0x90f8, + 0x9100, 0x9124, 0x9400, 0x9470, - 0x9600, 0x971c, + 0x9600, 0x9600, + 0x9608, 0x9638, + 0x9640, 0x9704, + 0x9710, 0x971c, 0x9800, 0x9808, 0x9820, 0x983c, 0x9850, 0x9864, @@ -1411,109 +2020,170 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x9f80, 0xa020, 0xd004, 0xd03c, 0xd100, 0xd118, - 0xd200, 0xd31c, + 0xd200, 0xd214, + 0xd220, 0xd234, + 0xd240, 0xd254, + 0xd260, 0xd274, + 0xd280, 0xd294, + 0xd2a0, 0xd2b4, + 0xd2c0, 0xd2d4, + 0xd2e0, 0xd2f4, + 0xd300, 0xd31c, 0xdfc0, 0xdfe0, 0xe000, 0xf008, 0x11000, 0x11014, - 0x11048, 0x1117c, - 0x11190, 0x11270, + 0x11048, 0x1106c, + 0x11074, 0x11088, + 0x11098, 0x11120, + 0x1112c, 0x1117c, + 0x11190, 0x112e0, 0x11300, 0x1130c, 0x12000, 0x1206c, 0x19040, 0x1906c, 0x19078, 0x19080, - 0x1908c, 0x19124, - 0x19150, 0x191b0, + 0x1908c, 0x190e8, + 0x190f0, 0x190f8, + 0x19100, 0x19110, + 0x19120, 0x19124, + 0x19150, 0x19194, + 0x1919c, 0x191b0, 0x191d0, 0x191e8, - 0x19238, 0x192bc, - 0x193f8, 0x19474, + 0x19238, 0x192b0, + 0x192bc, 0x192bc, + 0x19348, 0x1934c, + 0x193f8, 0x19418, + 0x19420, 0x19428, + 0x19430, 0x19444, + 0x1944c, 0x1946c, + 0x19474, 0x19474, 0x19490, 0x194cc, 0x194f0, 0x194f8, - 0x19c00, 0x19c80, - 0x19c94, 0x19cbc, - 0x19ce4, 0x19d28, + 0x19c00, 0x19c48, + 0x19c50, 0x19c80, + 0x19c94, 0x19c98, + 0x19ca0, 0x19cbc, + 0x19ce4, 0x19ce4, + 0x19cf0, 0x19cf8, + 0x19d00, 0x19d28, 0x19d50, 0x19d78, - 0x19d94, 0x19dc8, + 0x19d94, 0x19d98, + 0x19da0, 0x19dc8, 0x19df0, 0x19e10, 0x19e50, 0x19e6c, - 0x19ea0, 0x19f34, + 0x19ea0, 0x19ebc, + 0x19ec4, 0x19ef4, + 0x19f04, 0x19f2c, + 0x19f34, 0x19f34, 0x19f40, 0x19f50, 0x19f90, 0x19fac, - 0x19fc4, 0x19fe4, - 0x1a000, 0x1a06c, - 0x1a0b0, 0x1a120, - 0x1a128, 0x1a138, + 0x19fc4, 0x19fc8, + 0x19fd0, 0x19fe4, + 0x1a000, 0x1a004, + 0x1a010, 0x1a06c, + 0x1a0b0, 0x1a0e4, + 0x1a0ec, 0x1a0f8, + 0x1a100, 0x1a108, + 0x1a114, 0x1a120, + 0x1a128, 0x1a130, + 0x1a138, 0x1a138, 0x1a190, 0x1a1c4, 0x1a1fc, 0x1a1fc, 0x1e008, 0x1e00c, - 0x1e040, 0x1e04c, + 0x1e040, 0x1e044, + 0x1e04c, 0x1e04c, 0x1e284, 0x1e290, 0x1e2c0, 0x1e2c0, 0x1e2e0, 0x1e2e0, 0x1e300, 0x1e384, 0x1e3c0, 0x1e3c8, 0x1e408, 0x1e40c, - 0x1e440, 0x1e44c, + 0x1e440, 0x1e444, + 0x1e44c, 0x1e44c, 0x1e684, 0x1e690, 0x1e6c0, 0x1e6c0, 0x1e6e0, 0x1e6e0, 0x1e700, 0x1e784, 0x1e7c0, 0x1e7c8, 0x1e808, 0x1e80c, - 0x1e840, 0x1e84c, + 0x1e840, 0x1e844, + 0x1e84c, 0x1e84c, 0x1ea84, 0x1ea90, 0x1eac0, 0x1eac0, 0x1eae0, 0x1eae0, 0x1eb00, 0x1eb84, 0x1ebc0, 0x1ebc8, 0x1ec08, 0x1ec0c, - 0x1ec40, 0x1ec4c, + 0x1ec40, 0x1ec44, + 0x1ec4c, 0x1ec4c, 0x1ee84, 0x1ee90, 0x1eec0, 0x1eec0, 0x1eee0, 0x1eee0, 0x1ef00, 0x1ef84, 0x1efc0, 0x1efc8, 0x1f008, 0x1f00c, - 0x1f040, 0x1f04c, + 0x1f040, 0x1f044, + 0x1f04c, 0x1f04c, 0x1f284, 0x1f290, 0x1f2c0, 0x1f2c0, 0x1f2e0, 0x1f2e0, 0x1f300, 0x1f384, 0x1f3c0, 0x1f3c8, 0x1f408, 0x1f40c, - 0x1f440, 0x1f44c, + 0x1f440, 0x1f444, + 0x1f44c, 0x1f44c, 0x1f684, 0x1f690, 0x1f6c0, 0x1f6c0, 0x1f6e0, 0x1f6e0, 0x1f700, 0x1f784, 0x1f7c0, 0x1f7c8, 0x1f808, 0x1f80c, - 0x1f840, 0x1f84c, + 0x1f840, 0x1f844, + 0x1f84c, 0x1f84c, 0x1fa84, 0x1fa90, 0x1fac0, 0x1fac0, 0x1fae0, 0x1fae0, 0x1fb00, 0x1fb84, 0x1fbc0, 0x1fbc8, 0x1fc08, 0x1fc0c, - 0x1fc40, 0x1fc4c, + 0x1fc40, 0x1fc44, + 0x1fc4c, 0x1fc4c, 0x1fe84, 0x1fe90, 0x1fec0, 0x1fec0, 0x1fee0, 0x1fee0, 0x1ff00, 0x1ff84, 0x1ffc0, 0x1ffc8, - 0x30000, 0x30070, - 0x30100, 0x301d0, + 0x30000, 0x30030, + 0x30038, 0x30038, + 0x30040, 0x30040, + 0x30048, 0x30048, + 0x30050, 0x30050, + 0x3005c, 0x30060, + 0x30068, 0x30068, + 0x30070, 0x30070, + 0x30100, 0x30168, + 0x30190, 0x301a0, + 0x301a8, 0x301b8, + 0x301c4, 0x301c8, + 0x301d0, 0x301d0, 0x30200, 0x30320, - 0x30400, 0x3052c, + 0x30400, 0x304b4, + 0x304c0, 0x3052c, 0x30540, 0x3061c, - 0x30800, 0x30890, + 0x30800, 0x308a0, 0x308c0, 0x30908, 0x30910, 0x309b8, 0x30a00, 0x30a04, - 0x30a0c, 0x30a2c, + 0x30a0c, 0x30a14, + 0x30a1c, 0x30a2c, 0x30a44, 0x30a50, - 0x30a74, 0x30c24, - 0x30d00, 0x30d3c, - 0x30d44, 0x30d7c, + 0x30a74, 0x30a74, + 0x30a7c, 0x30afc, + 0x30b08, 0x30c24, + 0x30d00, 0x30d14, + 0x30d1c, 0x30d3c, + 0x30d44, 0x30d4c, + 0x30d54, 0x30d74, + 0x30d7c, 0x30d7c, 0x30de0, 0x30de0, 0x30e00, 0x30ed4, 0x30f00, 0x30fa4, @@ -1542,7 +2212,8 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x31bb0, 0x31bb4, 0x31bc8, 0x31bd4, 0x32140, 0x3218c, - 0x321f0, 0x32200, + 0x321f0, 0x321f4, + 0x32200, 0x32200, 0x32218, 0x32218, 0x32400, 0x32400, 0x32408, 0x3241c, @@ -1551,46 +2222,108 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x326a8, 0x326a8, 0x326ec, 0x326ec, 0x32a00, 0x32abc, - 0x32b00, 0x32b78, + 0x32b00, 0x32b38, + 0x32b40, 0x32b58, + 0x32b60, 0x32b78, 0x32c00, 0x32c00, 0x32c08, 0x32c3c, 0x32e00, 0x32e2c, 0x32f00, 0x32f2c, - 0x33000, 0x330ac, - 0x330c0, 0x331ac, - 0x331c0, 0x332c4, - 0x332e4, 0x333c4, - 0x333e4, 0x334ac, - 0x334c0, 0x335ac, - 0x335c0, 0x336c4, - 0x336e4, 0x337c4, + 0x33000, 0x3302c, + 0x33034, 0x33050, + 0x33058, 0x33058, + 0x33060, 0x3308c, + 0x3309c, 0x330ac, + 0x330c0, 0x330c0, + 0x330c8, 0x330d0, + 0x330d8, 0x330e0, + 0x330ec, 0x3312c, + 0x33134, 0x33150, + 0x33158, 0x33158, + 0x33160, 0x3318c, + 0x3319c, 0x331ac, + 0x331c0, 0x331c0, + 0x331c8, 0x331d0, + 0x331d8, 0x331e0, + 0x331ec, 0x33290, + 0x33298, 0x332c4, + 0x332e4, 0x33390, + 0x33398, 0x333c4, + 0x333e4, 0x3342c, + 0x33434, 0x33450, + 0x33458, 0x33458, + 0x33460, 0x3348c, + 0x3349c, 0x334ac, + 0x334c0, 0x334c0, + 0x334c8, 0x334d0, + 0x334d8, 0x334e0, + 0x334ec, 0x3352c, + 0x33534, 0x33550, + 0x33558, 0x33558, + 0x33560, 0x3358c, + 0x3359c, 0x335ac, + 0x335c0, 0x335c0, + 0x335c8, 0x335d0, + 0x335d8, 0x335e0, + 0x335ec, 0x33690, + 0x33698, 0x336c4, + 0x336e4, 0x33790, + 0x33798, 0x337c4, 0x337e4, 0x337fc, 0x33814, 0x33814, 0x33854, 0x33868, 0x33880, 0x3388c, 0x338c0, 0x338d0, 0x338e8, 0x338ec, - 0x33900, 0x339ac, - 0x339c0, 0x33ac4, + 0x33900, 0x3392c, + 0x33934, 0x33950, + 0x33958, 0x33958, + 0x33960, 0x3398c, + 0x3399c, 0x339ac, + 0x339c0, 0x339c0, + 0x339c8, 0x339d0, + 0x339d8, 0x339e0, + 0x339ec, 0x33a90, + 0x33a98, 0x33ac4, 0x33ae4, 0x33b10, - 0x33b24, 0x33b50, + 0x33b24, 0x33b28, + 0x33b38, 0x33b50, 0x33bf0, 0x33c10, - 0x33c24, 0x33c50, + 0x33c24, 0x33c28, + 0x33c38, 0x33c50, 0x33cf0, 0x33cfc, - 0x34000, 0x34070, - 0x34100, 0x341d0, + 0x34000, 0x34030, + 0x34038, 0x34038, + 0x34040, 0x34040, + 0x34048, 0x34048, + 0x34050, 0x34050, + 0x3405c, 0x34060, + 0x34068, 0x34068, + 0x34070, 0x34070, + 0x34100, 0x34168, + 0x34190, 0x341a0, + 0x341a8, 0x341b8, + 0x341c4, 0x341c8, + 0x341d0, 0x341d0, 0x34200, 0x34320, - 0x34400, 0x3452c, + 0x34400, 0x344b4, + 0x344c0, 0x3452c, 0x34540, 0x3461c, - 0x34800, 0x34890, + 0x34800, 0x348a0, 0x348c0, 0x34908, 0x34910, 0x349b8, 0x34a00, 0x34a04, - 0x34a0c, 0x34a2c, + 0x34a0c, 0x34a14, + 0x34a1c, 0x34a2c, 0x34a44, 0x34a50, - 0x34a74, 0x34c24, - 0x34d00, 0x34d3c, - 0x34d44, 0x34d7c, + 0x34a74, 0x34a74, + 0x34a7c, 0x34afc, + 0x34b08, 0x34c24, + 0x34d00, 0x34d14, + 0x34d1c, 0x34d3c, + 0x34d44, 0x34d4c, + 0x34d54, 0x34d74, + 0x34d7c, 0x34d7c, 0x34de0, 0x34de0, 0x34e00, 0x34ed4, 0x34f00, 0x34fa4, @@ -1619,7 +2352,8 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x35bb0, 0x35bb4, 0x35bc8, 0x35bd4, 0x36140, 0x3618c, - 0x361f0, 0x36200, + 0x361f0, 0x361f4, + 0x36200, 0x36200, 0x36218, 0x36218, 0x36400, 0x36400, 0x36408, 0x3641c, @@ -1628,31 +2362,75 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x366a8, 0x366a8, 0x366ec, 0x366ec, 0x36a00, 0x36abc, - 0x36b00, 0x36b78, + 0x36b00, 0x36b38, + 0x36b40, 0x36b58, + 0x36b60, 0x36b78, 0x36c00, 0x36c00, 0x36c08, 0x36c3c, 0x36e00, 0x36e2c, 0x36f00, 0x36f2c, - 0x37000, 0x370ac, - 0x370c0, 0x371ac, - 0x371c0, 0x372c4, - 0x372e4, 0x373c4, - 0x373e4, 0x374ac, - 0x374c0, 0x375ac, - 0x375c0, 0x376c4, - 0x376e4, 0x377c4, + 0x37000, 0x3702c, + 0x37034, 0x37050, + 0x37058, 0x37058, + 0x37060, 0x3708c, + 0x3709c, 0x370ac, + 0x370c0, 0x370c0, + 0x370c8, 0x370d0, + 0x370d8, 0x370e0, + 0x370ec, 0x3712c, + 0x37134, 0x37150, + 0x37158, 0x37158, + 0x37160, 0x3718c, + 0x3719c, 0x371ac, + 0x371c0, 0x371c0, + 0x371c8, 0x371d0, + 0x371d8, 0x371e0, + 0x371ec, 0x37290, + 0x37298, 0x372c4, + 0x372e4, 0x37390, + 0x37398, 0x373c4, + 0x373e4, 0x3742c, + 0x37434, 0x37450, + 0x37458, 0x37458, + 0x37460, 0x3748c, + 0x3749c, 0x374ac, + 0x374c0, 0x374c0, + 0x374c8, 0x374d0, + 0x374d8, 0x374e0, + 0x374ec, 0x3752c, + 0x37534, 0x37550, + 0x37558, 0x37558, + 0x37560, 0x3758c, + 0x3759c, 0x375ac, + 0x375c0, 0x375c0, + 0x375c8, 0x375d0, + 0x375d8, 0x375e0, + 0x375ec, 0x37690, + 0x37698, 0x376c4, + 0x376e4, 0x37790, + 0x37798, 0x377c4, 0x377e4, 0x377fc, 0x37814, 0x37814, 0x37854, 0x37868, 0x37880, 0x3788c, 0x378c0, 0x378d0, 0x378e8, 0x378ec, - 0x37900, 0x379ac, - 0x379c0, 0x37ac4, + 0x37900, 0x3792c, + 0x37934, 0x37950, + 0x37958, 0x37958, + 0x37960, 0x3798c, + 0x3799c, 0x379ac, + 0x379c0, 0x379c0, + 0x379c8, 0x379d0, + 0x379d8, 0x379e0, + 0x379ec, 0x37a90, + 0x37a98, 0x37ac4, 0x37ae4, 0x37b10, - 0x37b24, 0x37b50, + 0x37b24, 0x37b28, + 0x37b38, 0x37b50, 0x37bf0, 0x37c10, - 0x37c24, 0x37c50, + 0x37c24, 0x37c28, + 0x37c38, 0x37c50, 0x37cf0, 0x37cfc, 0x40040, 0x40040, 0x40080, 0x40084, @@ -1664,36 +2442,62 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x40280, 0x40280, 0x40304, 0x40304, 0x40330, 0x4033c, - 0x41304, 0x413dc, - 0x41400, 0x4141c, + 0x41304, 0x413c8, + 0x413d0, 0x413dc, + 0x413f0, 0x413f0, + 0x41400, 0x4140c, + 0x41414, 0x4141c, 0x41480, 0x414d0, 0x44000, 0x4407c, - 0x440c0, 0x4427c, - 0x442c0, 0x4447c, - 0x444c0, 0x4467c, - 0x446c0, 0x4487c, - 0x448c0, 0x44a7c, - 0x44ac0, 0x44c7c, - 0x44cc0, 0x44e7c, - 0x44ec0, 0x4507c, - 0x450c0, 0x451fc, - 0x45800, 0x45868, + 0x440c0, 0x441ac, + 0x441b4, 0x4427c, + 0x442c0, 0x443ac, + 0x443b4, 0x4447c, + 0x444c0, 0x445ac, + 0x445b4, 0x4467c, + 0x446c0, 0x447ac, + 0x447b4, 0x4487c, + 0x448c0, 0x449ac, + 0x449b4, 0x44a7c, + 0x44ac0, 0x44bac, + 0x44bb4, 0x44c7c, + 0x44cc0, 0x44dac, + 0x44db4, 0x44e7c, + 0x44ec0, 0x44fac, + 0x44fb4, 0x4507c, + 0x450c0, 0x451ac, + 0x451b4, 0x451fc, + 0x45800, 0x45804, + 0x45810, 0x45830, + 0x45840, 0x45860, + 0x45868, 0x45868, 0x45880, 0x45884, 0x458a0, 0x458b0, - 0x45a00, 0x45a68, + 0x45a00, 0x45a04, + 0x45a10, 0x45a30, + 0x45a40, 0x45a60, + 0x45a68, 0x45a68, 0x45a80, 0x45a84, 0x45aa0, 0x45ab0, 0x460c0, 0x460e4, - 0x47000, 0x4708c, + 0x47000, 0x4703c, + 0x47044, 0x4708c, 0x47200, 0x47250, - 0x47400, 0x47420, + 0x47400, 0x47408, + 0x47414, 0x47420, 0x47600, 0x47618, - 0x47800, 0x4782c, - 0x50000, 0x500cc, + 0x47800, 0x47814, + 0x47820, 0x4782c, + 0x50000, 0x50084, + 0x50090, 0x500cc, + 0x50300, 0x50384, 0x50400, 0x50400, - 0x50800, 0x508cc, + 0x50800, 0x50884, + 0x50890, 0x508cc, + 0x50b00, 0x50b84, 0x50c00, 0x50c00, - 0x51000, 0x510b0, + 0x51000, 0x51020, + 0x51028, 0x510b0, 0x51300, 0x51324, }; -- cgit v0.10.2 From b3695540ba5d8cf032f30533d53786b31af271f5 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Thu, 1 Oct 2015 13:48:46 +0530 Subject: cxgb4: For T4, don't read the Firmware Mailbox Control register T4 doesn't have the Shadow copy of the register which we can read without side effect. So don't read mbox control register for T4 adapter Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index 0a87a32..b4ace19 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -1128,18 +1128,26 @@ static const struct file_operations devlog_fops = { static int mbox_show(struct seq_file *seq, void *v) { static const char * const owner[] = { "none", "FW", "driver", - "unknown" }; + "unknown", "" }; int i; unsigned int mbox = (uintptr_t)seq->private & 7; struct adapter *adap = seq->private - mbox; void __iomem *addr = adap->regs + PF_REG(mbox, CIM_PF_MAILBOX_DATA_A); - unsigned int ctrl_reg = (is_t4(adap->params.chip) - ? CIM_PF_MAILBOX_CTRL_A - : CIM_PF_MAILBOX_CTRL_SHADOW_COPY_A); - void __iomem *ctrl = adap->regs + PF_REG(mbox, ctrl_reg); - i = MBOWNER_G(readl(ctrl)); + /* For T4 we don't have a shadow copy of the Mailbox Control register. + * And since reading that real register causes a side effect of + * granting ownership, we're best of simply not reading it at all. + */ + if (is_t4(adap->params.chip)) { + i = 4; /* index of "" */ + } else { + unsigned int ctrl_reg = CIM_PF_MAILBOX_CTRL_SHADOW_COPY_A; + void __iomem *ctrl = adap->regs + PF_REG(mbox, ctrl_reg); + + i = MBOWNER_G(readl(ctrl)); + } + seq_printf(seq, "mailbox owned by %s\n\n", owner[i]); for (i = 0; i < MBOX_LEN; i += 8) -- cgit v0.10.2 From da4976e17bff3bb7538090c36750b9875da67b5e Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Thu, 1 Oct 2015 13:48:47 +0530 Subject: cxgb4: Adds a new Device Log Facility FW_DEVLOG_FACILITY_CF The firmware team added a new Device Log Facility FW_DEVLOG_FACILITY_CF, but the driver has been decoding Device Log messages with that Facility as "(NULL)", fixing it. Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index b4ace19..4269944 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -940,6 +940,7 @@ static const char * const devlog_level_strings[] = { static const char * const devlog_facility_strings[] = { [FW_DEVLOG_FACILITY_CORE] = "CORE", + [FW_DEVLOG_FACILITY_CF] = "CF", [FW_DEVLOG_FACILITY_SCHED] = "SCHED", [FW_DEVLOG_FACILITY_TIMER] = "TIMER", [FW_DEVLOG_FACILITY_RES] = "RES", -- cgit v0.10.2 From 85412255ef4fcccf52e0408d61cf8a75828a6800 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Thu, 1 Oct 2015 13:48:48 +0530 Subject: cxgb4: Report correct link speed for unsupported ones When we get garbage from the firmware with weird Port Speeds, etc. we should emit a warning regarding unsupported speeds rather than use the bogus default of "10Mbps" which isn't even an option in the firmware Port Information message Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 0b7570d..9f1f5b2 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -275,7 +275,7 @@ static void link_report(struct net_device *dev) else { static const char *fc[] = { "no", "Rx", "Tx", "Tx/Rx" }; - const char *s = "10Mbps"; + const char *s; const struct port_info *p = netdev_priv(dev); switch (p->link_cfg.speed) { @@ -291,6 +291,10 @@ static void link_report(struct net_device *dev) case 40000: s = "40Gbps"; break; + default: + pr_info("%s: unsupported speed: %d\n", + dev->name, p->link_cfg.speed); + return; } netdev_info(dev, "link up, %s, full-duplex, %s PAUSE\n", s, -- cgit v0.10.2 From cc809237e1f215141165f221ad729803d88bcbbe Mon Sep 17 00:00:00 2001 From: Govindarajulu Varadarajan <_govind@gmx.com> Date: Thu, 1 Oct 2015 14:18:46 +0530 Subject: enic: handle spurious error interrupt Some of the enic adaptors are know to generate spurious interrupts. When error interrupt is generated, driver just resets the device. This patch resets the device only when an error is occurred. Signed-off-by: Govindarajulu Varadarajan <_govind@gmx.com> Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 3352d02..0b54429 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -178,13 +178,15 @@ static int enic_wq_service(struct vnic_dev *vdev, struct cq_desc *cq_desc, return 0; } -static void enic_log_q_error(struct enic *enic) +static bool enic_log_q_error(struct enic *enic) { unsigned int i; u32 error_status; + bool err = false; for (i = 0; i < enic->wq_count; i++) { error_status = vnic_wq_error_status(&enic->wq[i]); + err |= error_status; if (error_status) netdev_err(enic->netdev, "WQ[%d] error_status %d\n", i, error_status); @@ -192,10 +194,13 @@ static void enic_log_q_error(struct enic *enic) for (i = 0; i < enic->rq_count; i++) { error_status = vnic_rq_error_status(&enic->rq[i]); + err |= error_status; if (error_status) netdev_err(enic->netdev, "RQ[%d] error_status %d\n", i, error_status); } + + return err; } static void enic_msglvl_check(struct enic *enic) @@ -333,10 +338,9 @@ static irqreturn_t enic_isr_msix_err(int irq, void *data) vnic_intr_return_all_credits(&enic->intr[intr]); - enic_log_q_error(enic); - - /* schedule recovery from WQ/RQ error */ - schedule_work(&enic->reset); + if (enic_log_q_error(enic)) + /* schedule recovery from WQ/RQ error */ + schedule_work(&enic->reset); return IRQ_HANDLED; } -- cgit v0.10.2 From 937317c7c1097aa878a5000e3aab616eb5c590c0 Mon Sep 17 00:00:00 2001 From: Govindarajulu Varadarajan <_govind@gmx.com> Date: Thu, 1 Oct 2015 14:18:47 +0530 Subject: enic: do hang reset only in case of tx timeout The current code invokes hang reset in case of error interrupt. We should hang reset only in case of tx timeout. This because of the way hang reset is implemented in firmware. Hang reset takes more firmware resources than soft reset. Adaptor does not generate error interrupt in case of tx timeout. Hang reset only in case of tx timeout, in .ndo_tx_timeout. Do soft reset otherwise. Introduce deferred work, enic_tx_hang_reset, to do hang reset. Signed-off-by: Govindarajulu Varadarajan <_govind@gmx.com> Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/cisco/enic/enic.h b/drivers/net/ethernet/cisco/enic/enic.h index 8b53f7d..6401ba99 100644 --- a/drivers/net/ethernet/cisco/enic/enic.h +++ b/drivers/net/ethernet/cisco/enic/enic.h @@ -143,6 +143,7 @@ struct enic { struct vnic_dev *vdev; struct timer_list notify_timer; struct work_struct reset; + struct work_struct tx_hang_reset; struct work_struct change_mtu_work; struct msix_entry msix_entry[ENIC_INTR_MAX]; struct enic_msix_entry msix[ENIC_INTR_MAX]; diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 0b54429..0c22fd0 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -808,7 +808,7 @@ static void enic_set_rx_mode(struct net_device *netdev) static void enic_tx_timeout(struct net_device *netdev) { struct enic *enic = netdev_priv(netdev); - schedule_work(&enic->reset); + schedule_work(&enic->tx_hang_reset); } static int enic_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) @@ -1928,6 +1928,19 @@ static int enic_dev_open(struct enic *enic) return err; } +static int enic_dev_soft_reset(struct enic *enic) +{ + int err; + + err = enic_dev_wait(enic->vdev, vnic_dev_soft_reset, + vnic_dev_soft_reset_done, 0); + if (err) + netdev_err(enic->netdev, "vNIC soft reset failed, err %d\n", + err); + + return err; +} + static int enic_dev_hang_reset(struct enic *enic) { int err; @@ -2064,6 +2077,26 @@ static void enic_reset(struct work_struct *work) rtnl_lock(); spin_lock(&enic->enic_api_lock); + enic_stop(enic->netdev); + enic_dev_soft_reset(enic); + enic_reset_addr_lists(enic); + enic_init_vnic_resources(enic); + enic_set_rss_nic_cfg(enic); + enic_dev_set_ig_vlan_rewrite_mode(enic); + enic_open(enic->netdev); + spin_unlock(&enic->enic_api_lock); + call_netdevice_notifiers(NETDEV_REBOOT, enic->netdev); + + rtnl_unlock(); +} + +static void enic_tx_hang_reset(struct work_struct *work) +{ + struct enic *enic = container_of(work, struct enic, tx_hang_reset); + + rtnl_lock(); + + spin_lock(&enic->enic_api_lock); enic_dev_hang_notify(enic); enic_stop(enic->netdev); enic_dev_hang_reset(enic); @@ -2587,6 +2620,7 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) enic_set_rx_coal_setting(enic); INIT_WORK(&enic->reset, enic_reset); + INIT_WORK(&enic->tx_hang_reset, enic_tx_hang_reset); INIT_WORK(&enic->change_mtu_work, enic_change_mtu_work); for (i = 0; i < enic->wq_count; i++) diff --git a/drivers/net/ethernet/cisco/enic/vnic_dev.c b/drivers/net/ethernet/cisco/enic/vnic_dev.c index a3badef..1ffd105 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_dev.c +++ b/drivers/net/ethernet/cisco/enic/vnic_dev.c @@ -659,14 +659,14 @@ int vnic_dev_open_done(struct vnic_dev *vdev, int *done) return 0; } -static int vnic_dev_soft_reset(struct vnic_dev *vdev, int arg) +int vnic_dev_soft_reset(struct vnic_dev *vdev, int arg) { u64 a0 = (u32)arg, a1 = 0; int wait = 1000; return vnic_dev_cmd(vdev, CMD_SOFT_RESET, &a0, &a1, wait); } -static int vnic_dev_soft_reset_done(struct vnic_dev *vdev, int *done) +int vnic_dev_soft_reset_done(struct vnic_dev *vdev, int *done) { u64 a0 = 0, a1 = 0; int wait = 1000; diff --git a/drivers/net/ethernet/cisco/enic/vnic_dev.h b/drivers/net/ethernet/cisco/enic/vnic_dev.h index b013b6a..54156c4 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_dev.h +++ b/drivers/net/ethernet/cisco/enic/vnic_dev.h @@ -155,7 +155,9 @@ int vnic_dev_deinit(struct vnic_dev *vdev); void vnic_dev_intr_coal_timer_info_default(struct vnic_dev *vdev); int vnic_dev_intr_coal_timer_info(struct vnic_dev *vdev); int vnic_dev_hang_reset(struct vnic_dev *vdev, int arg); +int vnic_dev_soft_reset(struct vnic_dev *vdev, int arg); int vnic_dev_hang_reset_done(struct vnic_dev *vdev, int *done); +int vnic_dev_soft_reset_done(struct vnic_dev *vdev, int *done); void vnic_dev_set_intr_mode(struct vnic_dev *vdev, enum vnic_dev_intr_mode intr_mode); enum vnic_dev_intr_mode vnic_dev_get_intr_mode(struct vnic_dev *vdev); -- cgit v0.10.2 From 7741c373cf3ea1f5383fa97fb7a640a429d3dd7c Mon Sep 17 00:00:00 2001 From: Jon Ringle Date: Thu, 1 Oct 2015 07:43:20 -0400 Subject: regmap: Allow installing custom reg_update_bits function This commit allows installing a custom reg_update_bits function for cases where the hardware provides a mechanism to set or clear register bits without a read/modify/write cycle. Such is the case with the Microchip ENCX24J600. Signed-off-by: Jon Ringle Signed-off-by: David S. Miller diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index cc55788..4036d7a 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -98,6 +98,9 @@ struct regmap { int (*reg_read)(void *context, unsigned int reg, unsigned int *val); int (*reg_write)(void *context, unsigned int reg, unsigned int val); + int (*reg_update_bits)(void *context, unsigned int reg, + unsigned int mask, unsigned int val, + bool *change, bool force_write); bool defer_caching; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index afaf562..70387c9 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -619,6 +619,7 @@ struct regmap *__regmap_init(struct device *dev, goto skip_format_initialization; } else { map->reg_read = _regmap_bus_read; + map->reg_update_bits = bus->reg_update_bits; } reg_endian = regmap_get_reg_endian(bus, config); @@ -2509,6 +2510,30 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, int ret; unsigned int tmp, orig; + if (map->reg_update_bits) { + ret = map->reg_update_bits(map->bus_context, reg, mask, val, + change, force_write); + if (ret != 0) + return ret; + + /* Fix up the cache by read/modify/write */ + if (!map->cache_bypass && !map->defer_caching) { + ret = regcache_read(map, reg, &orig); + if (ret != 0) + return ret; + + tmp = orig & ~mask; + tmp |= val & mask; + + ret = regcache_write(map, reg, tmp); + if (ret != 0) + return ret; + if (map->cache_only) + map->cache_dirty = true; + } + return ret; + } + ret = _regmap_read(map, reg, &orig); if (ret != 0) return ret; diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 8fc0bfd..4d3a3b1 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -296,6 +296,9 @@ typedef int (*regmap_hw_reg_read)(void *context, unsigned int reg, unsigned int *val); typedef int (*regmap_hw_reg_write)(void *context, unsigned int reg, unsigned int val); +typedef int (*regmap_hw_reg_update_bits)(void *context, unsigned int reg, + unsigned int mask, unsigned int val, + bool *change, bool force_write); typedef struct regmap_async *(*regmap_hw_async_alloc)(void); typedef void (*regmap_hw_free_context)(void *context); @@ -335,6 +338,7 @@ struct regmap_bus { regmap_hw_gather_write gather_write; regmap_hw_async_write async_write; regmap_hw_reg_write reg_write; + regmap_hw_reg_update_bits reg_update_bits; regmap_hw_read read; regmap_hw_reg_read reg_read; regmap_hw_free_context free_context; -- cgit v0.10.2 From 04fbfce7a222327b97ca165294ef19f0faa45960 Mon Sep 17 00:00:00 2001 From: Jon Ringle Date: Thu, 1 Oct 2015 07:43:21 -0400 Subject: net: Microchip encx24j600 driver This ethernet driver supports the Micorchip enc424j600/626j600 Ethernet controller over a SPI bus interface. This driver makes use of the regmap API to optimize access to registers by caching registers where possible. Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/39935b.pdf Signed-off-by: Jon Ringle Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/microchip/Kconfig b/drivers/net/ethernet/microchip/Kconfig index 3fd8ca6..992b014 100644 --- a/drivers/net/ethernet/microchip/Kconfig +++ b/drivers/net/ethernet/microchip/Kconfig @@ -33,4 +33,13 @@ config ENC28J60_WRITEVERIFY Enable the verify after the buffer write useful for debugging purpose. If unsure, say N. +config ENCX24J600 + tristate "ENCX24J600 support" + depends on SPI + ---help--- + Support for the Microchip ENC424J600 ethernet chip. + + To compile this driver as a module, choose M here. The module will be + called enc424j600. + endif # NET_VENDOR_MICROCHIP diff --git a/drivers/net/ethernet/microchip/Makefile b/drivers/net/ethernet/microchip/Makefile index 573d429..ff78f62 100644 --- a/drivers/net/ethernet/microchip/Makefile +++ b/drivers/net/ethernet/microchip/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_ENC28J60) += enc28j60.o +obj-$(CONFIG_ENCX24J600) += encx24j600.o encx24j600-regmap.o diff --git a/drivers/net/ethernet/microchip/encx24j600-regmap.c b/drivers/net/ethernet/microchip/encx24j600-regmap.c new file mode 100644 index 0000000..f1d74e3 --- /dev/null +++ b/drivers/net/ethernet/microchip/encx24j600-regmap.c @@ -0,0 +1,551 @@ +/** + * Register map access API - ENCX24J600 support + * + * Copyright 2015 Gridpoint + * + * Author: Jon Ringle + * + * 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 "encx24j600_hw.h" + +static inline bool is_bits_set(int value, int mask) +{ + return (value & mask) == mask; +} + +static int encx24j600_switch_bank(struct encx24j600_context *ctx, + int bank) +{ + int ret = 0; + + int bank_opcode = BANK_SELECT(bank); + ret = spi_write(ctx->spi, &bank_opcode, 1); + if (ret == 0) + ctx->bank = bank; + + return ret; +} + +static int encx24j600_cmdn(struct encx24j600_context *ctx, u8 opcode, + const void *buf, size_t len) +{ + struct spi_message m; + struct spi_transfer t[2] = { { .tx_buf = &opcode, .len = 1, }, + { .tx_buf = buf, .len = len }, }; + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + + return spi_sync(ctx->spi, &m); +} + +static void regmap_lock_mutex(void *context) +{ + struct encx24j600_context *ctx = context; + mutex_lock(&ctx->mutex); +} + +static void regmap_unlock_mutex(void *context) +{ + struct encx24j600_context *ctx = context; + mutex_unlock(&ctx->mutex); +} + +static int regmap_encx24j600_sfr_read(void *context, u8 reg, u8 *val, + size_t len) +{ + struct encx24j600_context *ctx = context; + u8 banked_reg = reg & ADDR_MASK; + u8 bank = ((reg & BANK_MASK) >> BANK_SHIFT); + u8 cmd = RCRU; + int ret = 0; + int i = 0; + u8 tx_buf[2]; + + if (reg < 0x80) { + cmd = RCRCODE | banked_reg; + if ((banked_reg < 0x16) && (ctx->bank != bank)) + ret = encx24j600_switch_bank(ctx, bank); + if (unlikely(ret)) + return ret; + } else { + /* Translate registers that are more effecient using + * 3-byte SPI commands + */ + switch (reg) { + case EGPRDPT: + cmd = RGPRDPT; break; + case EGPWRPT: + cmd = RGPWRPT; break; + case ERXRDPT: + cmd = RRXRDPT; break; + case ERXWRPT: + cmd = RRXWRPT; break; + case EUDARDPT: + cmd = RUDARDPT; break; + case EUDAWRPT: + cmd = RUDAWRPT; break; + case EGPDATA: + case ERXDATA: + case EUDADATA: + default: + return -EINVAL; + } + } + + tx_buf[i++] = cmd; + if (cmd == RCRU) + tx_buf[i++] = reg; + + ret = spi_write_then_read(ctx->spi, tx_buf, i, val, len); + + return ret; +} + +static int regmap_encx24j600_sfr_update(struct encx24j600_context *ctx, + u8 reg, u8 *val, size_t len, + u8 unbanked_cmd, u8 banked_code) +{ + u8 banked_reg = reg & ADDR_MASK; + u8 bank = ((reg & BANK_MASK) >> BANK_SHIFT); + u8 cmd = unbanked_cmd; + struct spi_message m; + struct spi_transfer t[3] = { { .tx_buf = &cmd, .len = sizeof(cmd), }, + { .tx_buf = ®, .len = sizeof(reg), }, + { .tx_buf = val, .len = len }, }; + + if (reg < 0x80) { + int ret = 0; + cmd = banked_code | banked_reg; + if ((banked_reg < 0x16) && (ctx->bank != bank)) + ret = encx24j600_switch_bank(ctx, bank); + if (unlikely(ret)) + return ret; + } else { + /* Translate registers that are more effecient using + * 3-byte SPI commands + */ + switch (reg) { + case EGPRDPT: + cmd = WGPRDPT; break; + case EGPWRPT: + cmd = WGPWRPT; break; + case ERXRDPT: + cmd = WRXRDPT; break; + case ERXWRPT: + cmd = WRXWRPT; break; + case EUDARDPT: + cmd = WUDARDPT; break; + case EUDAWRPT: + cmd = WUDAWRPT; break; + case EGPDATA: + case ERXDATA: + case EUDADATA: + default: + return -EINVAL; + } + } + + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + + if (cmd == unbanked_cmd) { + t[1].tx_buf = ® + spi_message_add_tail(&t[1], &m); + } + + spi_message_add_tail(&t[2], &m); + return spi_sync(ctx->spi, &m); +} + +static int regmap_encx24j600_sfr_write(void *context, u8 reg, u8 *val, + size_t len) +{ + struct encx24j600_context *ctx = context; + return regmap_encx24j600_sfr_update(ctx, reg, val, len, WCRU, WCRCODE); +} + +static int regmap_encx24j600_sfr_set_bits(struct encx24j600_context *ctx, + u8 reg, u8 val) +{ + return regmap_encx24j600_sfr_update(ctx, reg, &val, 1, BFSU, BFSCODE); +} + +static int regmap_encx24j600_sfr_clr_bits(struct encx24j600_context *ctx, + u8 reg, u8 val) +{ + return regmap_encx24j600_sfr_update(ctx, reg, &val, 1, BFCU, BFCCODE); +} + +static int regmap_encx24j600_reg_update_bits(void *context, unsigned int reg, + unsigned int mask, + unsigned int val, bool *change, + bool force_write) +{ + struct encx24j600_context *ctx = context; + + int ret = 0; + unsigned int set_mask = mask & val; + unsigned int clr_mask = mask & ~val; + + if (change) + *change = false; + + if ((reg >= 0x40 && reg < 0x6c) || reg >= 0x80) { + /* Must do read/modify/write cycles for + * MAC/MII regs or Unbanked SFR regs + */ + u16 tmp, orig; + + ret = regmap_encx24j600_sfr_read(context, reg, (u8 *)&orig, + sizeof(orig)); + if (ret != 0) + return ret; + + tmp = orig & ~mask; + tmp |= val & mask; + + if (force_write || (tmp != orig)) { + ret = regmap_encx24j600_sfr_write(context, reg, + (u8 *)&tmp, + sizeof(tmp)); + if (change) + *change = true; + } else if (change) { + *change = false; + } + + return ret; + } + + if (set_mask & 0xff) { + ret = regmap_encx24j600_sfr_set_bits(ctx, reg, set_mask); + if (ret == 0 && change) + *change = true; + } + set_mask = (set_mask & 0xff00) >> 8; + + if ((set_mask & 0xff) && (ret == 0)) { + ret = regmap_encx24j600_sfr_set_bits(ctx, reg + 1, set_mask); + if (ret == 0 && change) + *change = true; + } + + if ((clr_mask & 0xff) && (ret == 0)) { + ret = regmap_encx24j600_sfr_clr_bits(ctx, reg, clr_mask); + if (ret == 0 && change) + *change = true; + } + clr_mask = (clr_mask & 0xff00) >> 8; + + if ((clr_mask & 0xff) && (ret == 0)) { + ret = regmap_encx24j600_sfr_clr_bits(ctx, reg + 1, clr_mask); + if (ret == 0 && change) + *change = true; + } + + return ret; +} + +int regmap_encx24j600_spi_write(void *context, u8 reg, const u8 *data, + size_t count) +{ + struct encx24j600_context *ctx = context; + + if (reg < 0xc0) + return encx24j600_cmdn(ctx, reg, data, count); + else + /* SPI 1-byte command. Ignore data */ + return spi_write(ctx->spi, ®, 1); +} +EXPORT_SYMBOL_GPL(regmap_encx24j600_spi_write); + +int regmap_encx24j600_spi_read(void *context, u8 reg, u8 *data, size_t count) +{ + struct encx24j600_context *ctx = context; + + if (reg == RBSEL && count > 1) + count = 1; + + return spi_write_then_read(ctx->spi, ®, sizeof(reg), data, count); +} +EXPORT_SYMBOL_GPL(regmap_encx24j600_spi_read); + +static int regmap_encx24j600_write(void *context, const void *data, + size_t len) +{ + u8 *dout = (u8 *)data; + u8 reg = dout[0]; + ++dout; + --len; + + if (reg > 0xa0) + return regmap_encx24j600_spi_write(context, reg, dout, len); + + if (len > 2) + return -EINVAL; + + return regmap_encx24j600_sfr_write(context, reg, dout, len); +} + +static int regmap_encx24j600_read(void *context, + const void *reg_buf, size_t reg_size, + void *val, size_t val_size) +{ + u8 reg = *(const u8 *)reg_buf; + + if (reg_size != 1) { + pr_err("%s: reg=%02x reg_size=%zu\n", __func__, reg, reg_size); + return -EINVAL; + } + + if (reg > 0xa0) + return regmap_encx24j600_spi_read(context, reg, val, val_size); + + if (val_size > 2) { + pr_err("%s: reg=%02x val_size=%zu\n", __func__, reg, val_size); + return -EINVAL; + } + + return regmap_encx24j600_sfr_read(context, reg, val, val_size); +} + +static bool encx24j600_regmap_readable(struct device *dev, unsigned int reg) +{ + if ((reg < 0x36) || + ((reg >= 0x40) && (reg < 0x4c)) || + ((reg >= 0x52) && (reg < 0x56)) || + ((reg >= 0x60) && (reg < 0x66)) || + ((reg >= 0x68) && (reg < 0x80)) || + ((reg >= 0x86) && (reg < 0x92)) || + (reg == 0xc8)) + return true; + else + return false; +} + +static bool encx24j600_regmap_writeable(struct device *dev, unsigned int reg) +{ + if ((reg < 0x12) || + ((reg >= 0x14) && (reg < 0x1a)) || + ((reg >= 0x1c) && (reg < 0x36)) || + ((reg >= 0x40) && (reg < 0x4c)) || + ((reg >= 0x52) && (reg < 0x56)) || + ((reg >= 0x60) && (reg < 0x68)) || + ((reg >= 0x6c) && (reg < 0x80)) || + ((reg >= 0x86) && (reg < 0x92)) || + ((reg >= 0xc0) && (reg < 0xc8)) || + ((reg >= 0xca) && (reg < 0xf0))) + return true; + else + return false; +} + +static bool encx24j600_regmap_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ERXHEAD: + case EDMACS: + case ETXSTAT: + case ETXWIRE: + case ECON1: /* Can be modified via single byte cmds */ + case ECON2: /* Can be modified via single byte cmds */ + case ESTAT: + case EIR: /* Can be modified via single byte cmds */ + case MIRD: + case MISTAT: + return true; + default: + break; + } + + return false; +} + +static bool encx24j600_regmap_precious(struct device *dev, unsigned int reg) +{ + /* single byte cmds are precious */ + if (((reg >= 0xc0) && (reg < 0xc8)) || + ((reg >= 0xca) && (reg < 0xf0))) + return true; + else + return false; +} + +static int regmap_encx24j600_phy_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct encx24j600_context *ctx = context; + int ret; + unsigned int mistat; + + reg = MIREGADR_VAL | (reg & PHREG_MASK); + ret = regmap_write(ctx->regmap, MIREGADR, reg); + if (unlikely(ret)) + goto err_out; + + ret = regmap_write(ctx->regmap, MICMD, MIIRD); + if (unlikely(ret)) + goto err_out; + + usleep_range(26, 100); + while ((ret = regmap_read(ctx->regmap, MISTAT, &mistat) != 0) && + (mistat & BUSY)) + cpu_relax(); + + if (unlikely(ret)) + goto err_out; + + ret = regmap_write(ctx->regmap, MICMD, 0); + if (unlikely(ret)) + goto err_out; + + ret = regmap_read(ctx->regmap, MIRD, val); + +err_out: + if (ret) + pr_err("%s: error %d reading reg %02x\n", __func__, ret, + reg & PHREG_MASK); + + return ret; +} + +static int regmap_encx24j600_phy_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct encx24j600_context *ctx = context; + int ret; + unsigned int mistat; + + reg = MIREGADR_VAL | (reg & PHREG_MASK); + ret = regmap_write(ctx->regmap, MIREGADR, reg); + if (unlikely(ret)) + goto err_out; + + ret = regmap_write(ctx->regmap, MIWR, val); + if (unlikely(ret)) + goto err_out; + + usleep_range(26, 100); + while ((ret = regmap_read(ctx->regmap, MISTAT, &mistat) != 0) && + (mistat & BUSY)) + cpu_relax(); + +err_out: + if (ret) + pr_err("%s: error %d writing reg %02x=%04x\n", __func__, ret, + reg & PHREG_MASK, val); + + return ret; +} + +static bool encx24j600_phymap_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PHCON1: + case PHSTAT1: + case PHANA: + case PHANLPA: + case PHANE: + case PHCON2: + case PHSTAT2: + case PHSTAT3: + return true; + default: + return false; + } +} + +static bool encx24j600_phymap_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PHCON1: + case PHCON2: + case PHANA: + return true; + case PHSTAT1: + case PHSTAT2: + case PHSTAT3: + case PHANLPA: + case PHANE: + default: + return false; + } +} + +static bool encx24j600_phymap_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PHSTAT1: + case PHSTAT2: + case PHSTAT3: + case PHANLPA: + case PHANE: + case PHCON2: + return true; + default: + return false; + } +} + +static struct regmap_config regcfg = { + .name = "reg", + .reg_bits = 8, + .val_bits = 16, + .max_register = 0xee, + .reg_stride = 2, + .cache_type = REGCACHE_RBTREE, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .readable_reg = encx24j600_regmap_readable, + .writeable_reg = encx24j600_regmap_writeable, + .volatile_reg = encx24j600_regmap_volatile, + .precious_reg = encx24j600_regmap_precious, + .lock = regmap_lock_mutex, + .unlock = regmap_unlock_mutex, +}; + +static struct regmap_bus regmap_encx24j600 = { + .write = regmap_encx24j600_write, + .read = regmap_encx24j600_read, + .reg_update_bits = regmap_encx24j600_reg_update_bits, +}; + +static struct regmap_config phycfg = { + .name = "phy", + .reg_bits = 8, + .val_bits = 16, + .max_register = 0x1f, + .cache_type = REGCACHE_RBTREE, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .readable_reg = encx24j600_phymap_readable, + .writeable_reg = encx24j600_phymap_writeable, + .volatile_reg = encx24j600_phymap_volatile, +}; +static struct regmap_bus phymap_encx24j600 = { + .reg_write = regmap_encx24j600_phy_reg_write, + .reg_read = regmap_encx24j600_phy_reg_read, +}; + +void devm_regmap_init_encx24j600(struct device *dev, + struct encx24j600_context *ctx) +{ + mutex_init(&ctx->mutex); + regcfg.lock_arg = ctx; + ctx->regmap = devm_regmap_init(dev, ®map_encx24j600, ctx, ®cfg); + ctx->phymap = devm_regmap_init(dev, &phymap_encx24j600, ctx, &phycfg); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_encx24j600); + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/microchip/encx24j600.c b/drivers/net/ethernet/microchip/encx24j600.c new file mode 100644 index 0000000..1da37fd --- /dev/null +++ b/drivers/net/ethernet/microchip/encx24j600.c @@ -0,0 +1,1124 @@ +/** + * Microchip ENCX24J600 ethernet driver + * + * Copyright (C) 2015 Gridpoint + * Author: Jon Ringle + * + * 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 "encx24j600_hw.h" + +#define DRV_NAME "encx24j600" +#define DRV_VERSION "1.0" + +#define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK) +static int debug = -1; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); + +/* SRAM memory layout: + * + * 0x0000-0x05ff TX buffers 1.5KB (1*1536) reside in the GP area in SRAM + * 0x0600-0x5fff RX buffers 22.5KB (15*1536) reside in the RX area in SRAM + */ +#define ENC_TX_BUF_START 0x0000U +#define ENC_RX_BUF_START 0x0600U +#define ENC_RX_BUF_END 0x5fffU +#define ENC_SRAM_SIZE 0x6000U + +enum { + RXFILTER_NORMAL, + RXFILTER_MULTI, + RXFILTER_PROMISC +}; + +struct encx24j600_priv { + struct net_device *ndev; + struct mutex lock; /* device access lock */ + struct encx24j600_context ctx; + struct sk_buff *tx_skb; + struct task_struct *kworker_task; + struct kthread_worker kworker; + struct kthread_work tx_work; + struct kthread_work setrx_work; + u16 next_packet; + bool hw_enabled; + bool full_duplex; + bool autoneg; + u16 speed; + int rxfilter; + u32 msg_enable; +}; + +static void dump_packet(const char *msg, int len, const char *data) +{ + pr_debug(DRV_NAME ": %s - packet len:%d\n", msg, len); + print_hex_dump_bytes("pk data: ", DUMP_PREFIX_OFFSET, data, len); +} + +static void encx24j600_dump_rsv(struct encx24j600_priv *priv, const char *msg, + struct rsv *rsv) +{ + struct net_device *dev = priv->ndev; + + netdev_info(dev, "RX packet Len:%d\n", rsv->len); + netdev_dbg(dev, "%s - NextPk: 0x%04x\n", msg, + rsv->next_packet); + netdev_dbg(dev, "RxOK: %d, DribbleNibble: %d\n", + RSV_GETBIT(rsv->rxstat, RSV_RXOK), + RSV_GETBIT(rsv->rxstat, RSV_DRIBBLENIBBLE)); + netdev_dbg(dev, "CRCErr:%d, LenChkErr: %d, LenOutOfRange: %d\n", + RSV_GETBIT(rsv->rxstat, RSV_CRCERROR), + RSV_GETBIT(rsv->rxstat, RSV_LENCHECKERR), + RSV_GETBIT(rsv->rxstat, RSV_LENOUTOFRANGE)); + netdev_dbg(dev, "Multicast: %d, Broadcast: %d, LongDropEvent: %d, CarrierEvent: %d\n", + RSV_GETBIT(rsv->rxstat, RSV_RXMULTICAST), + RSV_GETBIT(rsv->rxstat, RSV_RXBROADCAST), + RSV_GETBIT(rsv->rxstat, RSV_RXLONGEVDROPEV), + RSV_GETBIT(rsv->rxstat, RSV_CARRIEREV)); + netdev_dbg(dev, "ControlFrame: %d, PauseFrame: %d, UnknownOp: %d, VLanTagFrame: %d\n", + RSV_GETBIT(rsv->rxstat, RSV_RXCONTROLFRAME), + RSV_GETBIT(rsv->rxstat, RSV_RXPAUSEFRAME), + RSV_GETBIT(rsv->rxstat, RSV_RXUNKNOWNOPCODE), + RSV_GETBIT(rsv->rxstat, RSV_RXTYPEVLAN)); +} + +static u16 encx24j600_read_reg(struct encx24j600_priv *priv, u8 reg) +{ + struct net_device *dev = priv->ndev; + unsigned int val = 0; + int ret = regmap_read(priv->ctx.regmap, reg, &val); + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d reading reg %02x\n", + __func__, ret, reg); + return val; +} + +static void encx24j600_write_reg(struct encx24j600_priv *priv, u8 reg, u16 val) +{ + struct net_device *dev = priv->ndev; + int ret = regmap_write(priv->ctx.regmap, reg, val); + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d writing reg %02x=%04x\n", + __func__, ret, reg, val); +} + +static void encx24j600_update_reg(struct encx24j600_priv *priv, u8 reg, + u16 mask, u16 val) +{ + struct net_device *dev = priv->ndev; + int ret = regmap_update_bits(priv->ctx.regmap, reg, mask, val); + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d updating reg %02x=%04x~%04x\n", + __func__, ret, reg, val, mask); +} + +static u16 encx24j600_read_phy(struct encx24j600_priv *priv, u8 reg) +{ + struct net_device *dev = priv->ndev; + unsigned int val = 0; + int ret = regmap_read(priv->ctx.phymap, reg, &val); + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d reading %02x\n", + __func__, ret, reg); + return val; +} + +static void encx24j600_write_phy(struct encx24j600_priv *priv, u8 reg, u16 val) +{ + struct net_device *dev = priv->ndev; + int ret = regmap_write(priv->ctx.phymap, reg, val); + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d writing reg %02x=%04x\n", + __func__, ret, reg, val); +} + +static void encx24j600_clr_bits(struct encx24j600_priv *priv, u8 reg, u16 mask) +{ + encx24j600_update_reg(priv, reg, mask, 0); +} + +static void encx24j600_set_bits(struct encx24j600_priv *priv, u8 reg, u16 mask) +{ + encx24j600_update_reg(priv, reg, mask, mask); +} + +static void encx24j600_cmd(struct encx24j600_priv *priv, u8 cmd) +{ + struct net_device *dev = priv->ndev; + int ret = regmap_write(priv->ctx.regmap, cmd, 0); + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d with cmd %02x\n", + __func__, ret, cmd); +} + +static int encx24j600_raw_read(struct encx24j600_priv *priv, u8 reg, u8 *data, + size_t count) +{ + int ret; + mutex_lock(&priv->ctx.mutex); + ret = regmap_encx24j600_spi_read(&priv->ctx, reg, data, count); + mutex_unlock(&priv->ctx.mutex); + + return ret; +} + +static int encx24j600_raw_write(struct encx24j600_priv *priv, u8 reg, + const u8 *data, size_t count) +{ + int ret; + mutex_lock(&priv->ctx.mutex); + ret = regmap_encx24j600_spi_write(&priv->ctx, reg, data, count); + mutex_unlock(&priv->ctx.mutex); + + return ret; +} + +static void encx24j600_update_phcon1(struct encx24j600_priv *priv) +{ + u16 phcon1 = encx24j600_read_phy(priv, PHCON1); + if (priv->autoneg == AUTONEG_ENABLE) { + phcon1 |= ANEN | RENEG; + } else { + phcon1 &= ~ANEN; + if (priv->speed == SPEED_100) + phcon1 |= SPD100; + else + phcon1 &= ~SPD100; + + if (priv->full_duplex) + phcon1 |= PFULDPX; + else + phcon1 &= ~PFULDPX; + } + encx24j600_write_phy(priv, PHCON1, phcon1); +} + +/* Waits for autonegotiation to complete. */ +static int encx24j600_wait_for_autoneg(struct encx24j600_priv *priv) +{ + struct net_device *dev = priv->ndev; + unsigned long timeout = jiffies + msecs_to_jiffies(2000); + u16 phstat1; + u16 estat; + int ret = 0; + + phstat1 = encx24j600_read_phy(priv, PHSTAT1); + while ((phstat1 & ANDONE) == 0) { + if (time_after(jiffies, timeout)) { + u16 phstat3; + + netif_notice(priv, drv, dev, "timeout waiting for autoneg done\n"); + + priv->autoneg = AUTONEG_DISABLE; + phstat3 = encx24j600_read_phy(priv, PHSTAT3); + priv->speed = (phstat3 & PHY3SPD100) + ? SPEED_100 : SPEED_10; + priv->full_duplex = (phstat3 & PHY3DPX) ? 1 : 0; + encx24j600_update_phcon1(priv); + netif_notice(priv, drv, dev, "Using parallel detection: %s/%s", + priv->speed == SPEED_100 ? "100" : "10", + priv->full_duplex ? "Full" : "Half"); + + return -ETIMEDOUT; + } + cpu_relax(); + phstat1 = encx24j600_read_phy(priv, PHSTAT1); + } + + estat = encx24j600_read_reg(priv, ESTAT); + if (estat & PHYDPX) { + encx24j600_set_bits(priv, MACON2, FULDPX); + encx24j600_write_reg(priv, MABBIPG, 0x15); + } else { + encx24j600_clr_bits(priv, MACON2, FULDPX); + encx24j600_write_reg(priv, MABBIPG, 0x12); + /* Max retransmittions attempt */ + encx24j600_write_reg(priv, MACLCON, 0x370f); + } + + return ret; +} + +/* Access the PHY to determine link status */ +static void encx24j600_check_link_status(struct encx24j600_priv *priv) +{ + struct net_device *dev = priv->ndev; + u16 estat; + + estat = encx24j600_read_reg(priv, ESTAT); + + if (estat & PHYLNK) { + if (priv->autoneg == AUTONEG_ENABLE) + encx24j600_wait_for_autoneg(priv); + + netif_carrier_on(dev); + netif_info(priv, ifup, dev, "link up\n"); + } else { + netif_info(priv, ifdown, dev, "link down\n"); + + /* Re-enable autoneg since we won't know what we might be + * connected to when the link is brought back up again. + */ + priv->autoneg = AUTONEG_ENABLE; + priv->full_duplex = true; + priv->speed = SPEED_100; + netif_carrier_off(dev); + } +} + +static void encx24j600_int_link_handler(struct encx24j600_priv *priv) +{ + struct net_device *dev = priv->ndev; + + netif_dbg(priv, intr, dev, "%s", __func__); + encx24j600_check_link_status(priv); + encx24j600_clr_bits(priv, EIR, LINKIF); +} + +static void encx24j600_tx_complete(struct encx24j600_priv *priv, bool err) +{ + struct net_device *dev = priv->ndev; + + mutex_lock(&priv->lock); + + if (err) + dev->stats.tx_errors++; + else + dev->stats.tx_packets++; + + dev->stats.tx_bytes += priv->tx_skb->len; + + encx24j600_clr_bits(priv, EIR, TXIF | TXABTIF); + + netif_dbg(priv, tx_done, dev, "TX Done%s\n", err ? ": Err" : ""); + + if (priv->tx_skb) { + dev_kfree_skb(priv->tx_skb); + priv->tx_skb = NULL; + } + + netif_wake_queue(dev); + + mutex_unlock(&priv->lock); +} + +static int encx24j600_receive_packet(struct encx24j600_priv *priv, + struct rsv *rsv) +{ + struct net_device *dev = priv->ndev; + struct sk_buff *skb = netdev_alloc_skb(dev, rsv->len + NET_IP_ALIGN); + if (!skb) { + pr_err_ratelimited("RX: OOM: packet dropped\n"); + dev->stats.rx_dropped++; + return -ENOMEM; + } + skb_reserve(skb, NET_IP_ALIGN); + encx24j600_raw_read(priv, RRXDATA, skb_put(skb, rsv->len), rsv->len); + + if (netif_msg_pktdata(priv)) + dump_packet("RX", skb->len, skb->data); + + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + skb->ip_summed = CHECKSUM_COMPLETE; + + /* Maintain stats */ + dev->stats.rx_packets++; + dev->stats.rx_bytes += rsv->len; + priv->next_packet = rsv->next_packet; + + netif_rx(skb); + + return 0; +} + +static void encx24j600_rx_packets(struct encx24j600_priv *priv, u8 packet_count) +{ + struct net_device *dev = priv->ndev; + + while (packet_count--) { + struct rsv rsv; + u16 newrxtail; + + encx24j600_write_reg(priv, ERXRDPT, priv->next_packet); + encx24j600_raw_read(priv, RRXDATA, (u8 *)&rsv, sizeof(rsv)); + + if (netif_msg_rx_status(priv)) + encx24j600_dump_rsv(priv, __func__, &rsv); + + if (!RSV_GETBIT(rsv.rxstat, RSV_RXOK) || + (rsv.len > MAX_FRAMELEN)) { + netif_err(priv, rx_err, dev, "RX Error %04x\n", + rsv.rxstat); + dev->stats.rx_errors++; + + if (RSV_GETBIT(rsv.rxstat, RSV_CRCERROR)) + dev->stats.rx_crc_errors++; + if (RSV_GETBIT(rsv.rxstat, RSV_LENCHECKERR)) + dev->stats.rx_frame_errors++; + if (rsv.len > MAX_FRAMELEN) + dev->stats.rx_over_errors++; + } else { + encx24j600_receive_packet(priv, &rsv); + } + + newrxtail = priv->next_packet - 2; + if (newrxtail == ENC_RX_BUF_START) + newrxtail = SRAM_SIZE - 2; + + encx24j600_cmd(priv, SETPKTDEC); + encx24j600_write_reg(priv, ERXTAIL, newrxtail); + } +} + +static irqreturn_t encx24j600_isr(int irq, void *dev_id) +{ + struct encx24j600_priv *priv = dev_id; + struct net_device *dev = priv->ndev; + int eir; + + /* Clear interrupts */ + encx24j600_cmd(priv, CLREIE); + + eir = encx24j600_read_reg(priv, EIR); + + if (eir & LINKIF) + encx24j600_int_link_handler(priv); + + if (eir & TXIF) + encx24j600_tx_complete(priv, false); + + if (eir & TXABTIF) + encx24j600_tx_complete(priv, true); + + if (eir & RXABTIF) { + if (eir & PCFULIF) { + /* Packet counter is full */ + netif_err(priv, rx_err, dev, "Packet counter full\n"); + } + dev->stats.rx_dropped++; + encx24j600_clr_bits(priv, EIR, RXABTIF); + } + + if (eir & PKTIF) { + u8 packet_count; + + mutex_lock(&priv->lock); + + packet_count = encx24j600_read_reg(priv, ESTAT) & 0xff; + while (packet_count) { + encx24j600_rx_packets(priv, packet_count); + packet_count = encx24j600_read_reg(priv, ESTAT) & 0xff; + } + + mutex_unlock(&priv->lock); + } + + /* Enable interrupts */ + encx24j600_cmd(priv, SETEIE); + + return IRQ_HANDLED; +} + +static int encx24j600_soft_reset(struct encx24j600_priv *priv) +{ + int ret = 0; + int timeout; + u16 eudast; + + /* Write and verify a test value to EUDAST */ + regcache_cache_bypass(priv->ctx.regmap, true); + timeout = 10; + do { + encx24j600_write_reg(priv, EUDAST, EUDAST_TEST_VAL); + eudast = encx24j600_read_reg(priv, EUDAST); + usleep_range(25, 100); + } while ((eudast != EUDAST_TEST_VAL) && --timeout); + regcache_cache_bypass(priv->ctx.regmap, false); + + if (timeout == 0) { + ret = -ETIMEDOUT; + goto err_out; + } + + /* Wait for CLKRDY to become set */ + timeout = 10; + while (!(encx24j600_read_reg(priv, ESTAT) & CLKRDY) && --timeout) + usleep_range(25, 100); + + if (timeout == 0) { + ret = -ETIMEDOUT; + goto err_out; + } + + /* Issue a System Reset command */ + encx24j600_cmd(priv, SETETHRST); + usleep_range(25, 100); + + /* Confirm that EUDAST has 0000h after system reset */ + if (encx24j600_read_reg(priv, EUDAST) != 0) { + ret = -EINVAL; + goto err_out; + } + + /* Wait for PHY register and status bits to become available */ + usleep_range(256, 1000); + +err_out: + return ret; +} + +static int encx24j600_hw_reset(struct encx24j600_priv *priv) +{ + int ret; + + mutex_lock(&priv->lock); + ret = encx24j600_soft_reset(priv); + mutex_unlock(&priv->lock); + + return ret; +} + +static void encx24j600_reset_hw_tx(struct encx24j600_priv *priv) +{ + encx24j600_set_bits(priv, ECON2, TXRST); + encx24j600_clr_bits(priv, ECON2, TXRST); +} + +static void encx24j600_hw_init_tx(struct encx24j600_priv *priv) +{ + /* Reset TX */ + encx24j600_reset_hw_tx(priv); + + /* Clear the TXIF flag if were previously set */ + encx24j600_clr_bits(priv, EIR, TXIF | TXABTIF); + + /* Write the Tx Buffer pointer */ + encx24j600_write_reg(priv, EGPWRPT, ENC_TX_BUF_START); +} + +static void encx24j600_hw_init_rx(struct encx24j600_priv *priv) +{ + encx24j600_cmd(priv, DISABLERX); + + /* Set up RX packet start address in the SRAM */ + encx24j600_write_reg(priv, ERXST, ENC_RX_BUF_START); + + /* Preload the RX Data pointer to the beginning of the RX area */ + encx24j600_write_reg(priv, ERXRDPT, ENC_RX_BUF_START); + + priv->next_packet = ENC_RX_BUF_START; + + /* Set up RX end address in the SRAM */ + encx24j600_write_reg(priv, ERXTAIL, ENC_SRAM_SIZE - 2); + + /* Reset the user data pointers */ + encx24j600_write_reg(priv, EUDAST, ENC_SRAM_SIZE); + encx24j600_write_reg(priv, EUDAND, ENC_SRAM_SIZE + 1); + + /* Set Max Frame length */ + encx24j600_write_reg(priv, MAMXFL, MAX_FRAMELEN); +} + +static void encx24j600_dump_config(struct encx24j600_priv *priv, + const char *msg) +{ + pr_info(DRV_NAME ": %s\n", msg); + + /* CHIP configuration */ + pr_info(DRV_NAME " ECON1: %04X\n", encx24j600_read_reg(priv, ECON1)); + pr_info(DRV_NAME " ECON2: %04X\n", encx24j600_read_reg(priv, ECON2)); + pr_info(DRV_NAME " ERXFCON: %04X\n", encx24j600_read_reg(priv, + ERXFCON)); + pr_info(DRV_NAME " ESTAT: %04X\n", encx24j600_read_reg(priv, ESTAT)); + pr_info(DRV_NAME " EIR: %04X\n", encx24j600_read_reg(priv, EIR)); + pr_info(DRV_NAME " EIDLED: %04X\n", encx24j600_read_reg(priv, EIDLED)); + + /* MAC layer configuration */ + pr_info(DRV_NAME " MACON1: %04X\n", encx24j600_read_reg(priv, MACON1)); + pr_info(DRV_NAME " MACON2: %04X\n", encx24j600_read_reg(priv, MACON2)); + pr_info(DRV_NAME " MAIPG: %04X\n", encx24j600_read_reg(priv, MAIPG)); + pr_info(DRV_NAME " MACLCON: %04X\n", encx24j600_read_reg(priv, + MACLCON)); + pr_info(DRV_NAME " MABBIPG: %04X\n", encx24j600_read_reg(priv, + MABBIPG)); + + /* PHY configuation */ + pr_info(DRV_NAME " PHCON1: %04X\n", encx24j600_read_phy(priv, PHCON1)); + pr_info(DRV_NAME " PHCON2: %04X\n", encx24j600_read_phy(priv, PHCON2)); + pr_info(DRV_NAME " PHANA: %04X\n", encx24j600_read_phy(priv, PHANA)); + pr_info(DRV_NAME " PHANLPA: %04X\n", encx24j600_read_phy(priv, + PHANLPA)); + pr_info(DRV_NAME " PHANE: %04X\n", encx24j600_read_phy(priv, PHANE)); + pr_info(DRV_NAME " PHSTAT1: %04X\n", encx24j600_read_phy(priv, + PHSTAT1)); + pr_info(DRV_NAME " PHSTAT2: %04X\n", encx24j600_read_phy(priv, + PHSTAT2)); + pr_info(DRV_NAME " PHSTAT3: %04X\n", encx24j600_read_phy(priv, + PHSTAT3)); +} + +static void encx24j600_set_rxfilter_mode(struct encx24j600_priv *priv) +{ + switch (priv->rxfilter) { + case RXFILTER_PROMISC: + encx24j600_set_bits(priv, MACON1, PASSALL); + encx24j600_write_reg(priv, ERXFCON, UCEN | MCEN | NOTMEEN); + break; + case RXFILTER_MULTI: + encx24j600_clr_bits(priv, MACON1, PASSALL); + encx24j600_write_reg(priv, ERXFCON, UCEN | CRCEN | BCEN | MCEN); + break; + case RXFILTER_NORMAL: + default: + encx24j600_clr_bits(priv, MACON1, PASSALL); + encx24j600_write_reg(priv, ERXFCON, UCEN | CRCEN | BCEN); + break; + } +} + +static int encx24j600_hw_init(struct encx24j600_priv *priv) +{ + struct net_device *dev = priv->ndev; + int ret = 0; + u16 eidled; + u16 macon2; + + priv->hw_enabled = false; + + eidled = encx24j600_read_reg(priv, EIDLED); + if (((eidled & DEVID_MASK) >> DEVID_SHIFT) != ENCX24J600_DEV_ID) { + ret = -EINVAL; + goto err_out; + } + + netif_info(priv, drv, dev, "Silicon rev ID: 0x%02x\n", + (eidled & REVID_MASK) >> REVID_SHIFT); + + /* PHY Leds: link status, + * LEDA: Link + transmit/receive events + * LEDB: Link State + colision events + */ + encx24j600_update_reg(priv, EIDLED, 0xbc00, 0xbc00); + + /* Loopback disabled */ + encx24j600_write_reg(priv, MACON1, 0x9); + + /* interpacket gap value */ + encx24j600_write_reg(priv, MAIPG, 0x0c12); + + /* Write the auto negotiation pattern */ + encx24j600_write_phy(priv, PHANA, PHANA_DEFAULT); + + encx24j600_update_phcon1(priv); + encx24j600_check_link_status(priv); + + macon2 = MACON2_RSV1 | TXCRCEN | PADCFG0 | PADCFG2 | MACON2_DEFER; + if ((priv->autoneg == AUTONEG_DISABLE) && priv->full_duplex) + macon2 |= FULDPX; + + encx24j600_set_bits(priv, MACON2, macon2); + + priv->rxfilter = RXFILTER_NORMAL; + encx24j600_set_rxfilter_mode(priv); + + /* Program the Maximum frame length */ + encx24j600_write_reg(priv, MAMXFL, MAX_FRAMELEN); + + /* Init Tx pointers */ + encx24j600_hw_init_tx(priv); + + /* Init Rx pointers */ + encx24j600_hw_init_rx(priv); + + if (netif_msg_hw(priv)) + encx24j600_dump_config(priv, "Hw is initialized"); + +err_out: + return ret; +} + +static void encx24j600_hw_enable(struct encx24j600_priv *priv) +{ + /* Clear the interrupt flags in case was set */ + encx24j600_clr_bits(priv, EIR, (PCFULIF | RXABTIF | TXABTIF | TXIF | + PKTIF | LINKIF)); + + /* Enable the interrupts */ + encx24j600_write_reg(priv, EIE, (PCFULIE | RXABTIE | TXABTIE | TXIE | + PKTIE | LINKIE | INTIE)); + + /* Enable RX */ + encx24j600_cmd(priv, ENABLERX); + + priv->hw_enabled = true; +} + +static void encx24j600_hw_disable(struct encx24j600_priv *priv) +{ + /* Disable all interrupts */ + encx24j600_write_reg(priv, EIE, 0); + + /* Disable RX */ + encx24j600_cmd(priv, DISABLERX); + + priv->hw_enabled = false; +} + +static int encx24j600_setlink(struct net_device *dev, u8 autoneg, u16 speed, + u8 duplex) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + int ret = 0; + + if (!priv->hw_enabled) { + /* link is in low power mode now; duplex setting + * will take effect on next encx24j600_hw_init() + */ + if (speed == SPEED_10 || speed == SPEED_100) { + priv->autoneg = (autoneg == AUTONEG_ENABLE); + priv->full_duplex = (duplex == DUPLEX_FULL); + priv->speed = (speed == SPEED_100); + } else { + netif_warn(priv, link, dev, "unsupported link speed setting\n"); + /*speeds other than SPEED_10 and SPEED_100 */ + /*are not supported by chip */ + ret = -EOPNOTSUPP; + } + } else { + netif_warn(priv, link, dev, "Warning: hw must be disabled to set link mode\n"); + ret = -EBUSY; + } + return ret; +} + +static void encx24j600_hw_get_macaddr(struct encx24j600_priv *priv, + unsigned char *ethaddr) +{ + unsigned short val; + + val = encx24j600_read_reg(priv, MAADR1); + + ethaddr[0] = val & 0x00ff; + ethaddr[1] = (val & 0xff00) >> 8; + + val = encx24j600_read_reg(priv, MAADR2); + + ethaddr[2] = val & 0x00ffU; + ethaddr[3] = (val & 0xff00U) >> 8; + + val = encx24j600_read_reg(priv, MAADR3); + + ethaddr[4] = val & 0x00ffU; + ethaddr[5] = (val & 0xff00U) >> 8; +} + +/* Program the hardware MAC address from dev->dev_addr.*/ +static int encx24j600_set_hw_macaddr(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + if (priv->hw_enabled) { + netif_info(priv, drv, dev, "Hardware must be disabled to set Mac address\n"); + return -EBUSY; + } + + mutex_lock(&priv->lock); + + netif_info(priv, drv, dev, "%s: Setting MAC address to %pM\n", + dev->name, dev->dev_addr); + + encx24j600_write_reg(priv, MAADR3, (dev->dev_addr[4] | + dev->dev_addr[5] << 8)); + encx24j600_write_reg(priv, MAADR2, (dev->dev_addr[2] | + dev->dev_addr[3] << 8)); + encx24j600_write_reg(priv, MAADR1, (dev->dev_addr[0] | + dev->dev_addr[1] << 8)); + + mutex_unlock(&priv->lock); + + return 0; +} + +/* Store the new hardware address in dev->dev_addr, and update the MAC.*/ +static int encx24j600_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *address = addr; + + if (netif_running(dev)) + return -EBUSY; + if (!is_valid_ether_addr(address->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(dev->dev_addr, address->sa_data, dev->addr_len); + return encx24j600_set_hw_macaddr(dev); +} + +static int encx24j600_open(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + int ret = request_threaded_irq(priv->ctx.spi->irq, NULL, encx24j600_isr, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + DRV_NAME, priv); + if (unlikely(ret < 0)) { + netdev_err(dev, "request irq %d failed (ret = %d)\n", + priv->ctx.spi->irq, ret); + return ret; + } + + encx24j600_hw_disable(priv); + encx24j600_hw_init(priv); + encx24j600_hw_enable(priv); + netif_start_queue(dev); + + return 0; +} + +static int encx24j600_stop(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + netif_stop_queue(dev); + free_irq(priv->ctx.spi->irq, priv); + return 0; +} + +static void encx24j600_setrx_proc(struct kthread_work *ws) +{ + struct encx24j600_priv *priv = + container_of(ws, struct encx24j600_priv, setrx_work); + + mutex_lock(&priv->lock); + encx24j600_set_rxfilter_mode(priv); + mutex_unlock(&priv->lock); +} + +static void encx24j600_set_multicast_list(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + int oldfilter = priv->rxfilter; + + if (dev->flags & IFF_PROMISC) { + netif_dbg(priv, link, dev, "promiscuous mode\n"); + priv->rxfilter = RXFILTER_PROMISC; + } else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev)) { + netif_dbg(priv, link, dev, "%smulticast mode\n", + (dev->flags & IFF_ALLMULTI) ? "all-" : ""); + priv->rxfilter = RXFILTER_MULTI; + } else { + netif_dbg(priv, link, dev, "normal mode\n"); + priv->rxfilter = RXFILTER_NORMAL; + } + + if (oldfilter != priv->rxfilter) + queue_kthread_work(&priv->kworker, &priv->setrx_work); +} + +static void encx24j600_hw_tx(struct encx24j600_priv *priv) +{ + struct net_device *dev = priv->ndev; + netif_info(priv, tx_queued, dev, "TX Packet Len:%d\n", + priv->tx_skb->len); + + if (netif_msg_pktdata(priv)) + dump_packet("TX", priv->tx_skb->len, priv->tx_skb->data); + + if (encx24j600_read_reg(priv, EIR) & TXABTIF) + /* Last transmition aborted due to error. Reset TX interface */ + encx24j600_reset_hw_tx(priv); + + /* Clear the TXIF flag if were previously set */ + encx24j600_clr_bits(priv, EIR, TXIF); + + /* Set the data pointer to the TX buffer address in the SRAM */ + encx24j600_write_reg(priv, EGPWRPT, ENC_TX_BUF_START); + + /* Copy the packet into the SRAM */ + encx24j600_raw_write(priv, WGPDATA, (u8 *)priv->tx_skb->data, + priv->tx_skb->len); + + /* Program the Tx buffer start pointer */ + encx24j600_write_reg(priv, ETXST, ENC_TX_BUF_START); + + /* Program the packet length */ + encx24j600_write_reg(priv, ETXLEN, priv->tx_skb->len); + + /* Start the transmission */ + encx24j600_cmd(priv, SETTXRTS); +} + +static void encx24j600_tx_proc(struct kthread_work *ws) +{ + struct encx24j600_priv *priv = + container_of(ws, struct encx24j600_priv, tx_work); + + mutex_lock(&priv->lock); + encx24j600_hw_tx(priv); + mutex_unlock(&priv->lock); +} + +static netdev_tx_t encx24j600_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + netif_stop_queue(dev); + + /* save the timestamp */ + dev->trans_start = jiffies; + + /* Remember the skb for deferred processing */ + priv->tx_skb = skb; + + queue_kthread_work(&priv->kworker, &priv->tx_work); + + return NETDEV_TX_OK; +} + +/* Deal with a transmit timeout */ +static void encx24j600_tx_timeout(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + netif_err(priv, tx_err, dev, "TX timeout at %ld, latency %ld\n", + jiffies, jiffies - dev->trans_start); + + dev->stats.tx_errors++; + netif_wake_queue(dev); + return; +} + +static int encx24j600_get_regs_len(struct net_device *dev) +{ + return SFR_REG_COUNT; +} + +static void encx24j600_get_regs(struct net_device *dev, + struct ethtool_regs *regs, void *p) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + u16 *buff = p; + u8 reg; + + regs->version = 1; + mutex_lock(&priv->lock); + for (reg = 0; reg < SFR_REG_COUNT; reg += 2) { + unsigned int val = 0; + /* ignore errors for unreadable registers */ + regmap_read(priv->ctx.regmap, reg, &val); + buff[reg] = val & 0xffff; + } + mutex_unlock(&priv->lock); +} + +static void encx24j600_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, dev_name(dev->dev.parent), + sizeof(info->bus_info)); +} + +static int encx24j600_get_settings(struct net_device *dev, + struct ethtool_cmd *cmd) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + cmd->transceiver = XCVR_INTERNAL; + cmd->supported = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg | SUPPORTED_TP; + + ethtool_cmd_speed_set(cmd, priv->speed); + cmd->duplex = priv->full_duplex ? DUPLEX_FULL : DUPLEX_HALF; + cmd->port = PORT_TP; + cmd->autoneg = priv->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE; + + return 0; +} + +static int encx24j600_set_settings(struct net_device *dev, + struct ethtool_cmd *cmd) +{ + return encx24j600_setlink(dev, cmd->autoneg, + ethtool_cmd_speed(cmd), cmd->duplex); +} + +static u32 encx24j600_get_msglevel(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + return priv->msg_enable; +} + +static void encx24j600_set_msglevel(struct net_device *dev, u32 val) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + priv->msg_enable = val; +} + +static const struct ethtool_ops encx24j600_ethtool_ops = { + .get_settings = encx24j600_get_settings, + .set_settings = encx24j600_set_settings, + .get_drvinfo = encx24j600_get_drvinfo, + .get_msglevel = encx24j600_get_msglevel, + .set_msglevel = encx24j600_set_msglevel, + .get_regs_len = encx24j600_get_regs_len, + .get_regs = encx24j600_get_regs, +}; + +static const struct net_device_ops encx24j600_netdev_ops = { + .ndo_open = encx24j600_open, + .ndo_stop = encx24j600_stop, + .ndo_start_xmit = encx24j600_tx, + .ndo_set_rx_mode = encx24j600_set_multicast_list, + .ndo_set_mac_address = encx24j600_set_mac_address, + .ndo_tx_timeout = encx24j600_tx_timeout, + .ndo_validate_addr = eth_validate_addr, +}; + +static int encx24j600_spi_probe(struct spi_device *spi) +{ + int ret; + + struct net_device *ndev; + struct encx24j600_priv *priv; + + ndev = alloc_etherdev(sizeof(struct encx24j600_priv)); + + if (!ndev) { + ret = -ENOMEM; + goto error_out; + } + + priv = netdev_priv(ndev); + spi_set_drvdata(spi, priv); + dev_set_drvdata(&spi->dev, priv); + SET_NETDEV_DEV(ndev, &spi->dev); + + priv->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE); + priv->ndev = ndev; + + /* Default configuration PHY configuration */ + priv->full_duplex = true; + priv->autoneg = AUTONEG_ENABLE; + priv->speed = SPEED_100; + + priv->ctx.spi = spi; + devm_regmap_init_encx24j600(&spi->dev, &priv->ctx); + ndev->irq = spi->irq; + ndev->netdev_ops = &encx24j600_netdev_ops; + + mutex_init(&priv->lock); + + /* Reset device and check if it is connected */ + if (encx24j600_hw_reset(priv)) { + netif_err(priv, probe, ndev, + DRV_NAME ": Chip is not detected\n"); + ret = -EIO; + goto out_free; + } + + /* Initialize the device HW to the consistent state */ + if (encx24j600_hw_init(priv)) { + netif_err(priv, probe, ndev, + DRV_NAME ": HW initialization error\n"); + ret = -EIO; + goto out_free; + } + + init_kthread_worker(&priv->kworker); + init_kthread_work(&priv->tx_work, encx24j600_tx_proc); + init_kthread_work(&priv->setrx_work, encx24j600_setrx_proc); + + priv->kworker_task = kthread_run(kthread_worker_fn, &priv->kworker, + "encx24j600"); + + if (IS_ERR(priv->kworker_task)) { + ret = PTR_ERR(priv->kworker_task); + goto out_free; + } + + /* Get the MAC address from the chip */ + encx24j600_hw_get_macaddr(priv, ndev->dev_addr); + + ndev->ethtool_ops = &encx24j600_ethtool_ops; + + ret = register_netdev(ndev); + if (unlikely(ret)) { + netif_err(priv, probe, ndev, "Error %d initializing card encx24j600 card\n", + ret); + goto out_free; + } + + netif_info(priv, drv, priv->ndev, "MAC address %pM\n", ndev->dev_addr); + + return ret; + +out_free: + free_netdev(ndev); + +error_out: + return ret; +} + +static int encx24j600_spi_remove(struct spi_device *spi) +{ + struct encx24j600_priv *priv = dev_get_drvdata(&spi->dev); + + unregister_netdev(priv->ndev); + + free_netdev(priv->ndev); + + return 0; +} + +static const struct spi_device_id encx24j600_spi_id_table = { + .name = "encx24j600" +}; + +static struct spi_driver encx24j600_spi_net_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .bus = &spi_bus_type, + }, + .probe = encx24j600_spi_probe, + .remove = encx24j600_spi_remove, + .id_table = &encx24j600_spi_id_table, +}; + +static int __init encx24j600_init(void) +{ + return spi_register_driver(&encx24j600_spi_net_driver); +} +module_init(encx24j600_init); + +void encx24j600_exit(void) +{ + spi_unregister_driver(&encx24j600_spi_net_driver); +} +module_exit(encx24j600_exit); + +MODULE_DESCRIPTION(DRV_NAME " ethernet driver"); +MODULE_AUTHOR("Jon Ringle "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:" DRV_NAME); diff --git a/drivers/net/ethernet/microchip/encx24j600_hw.h b/drivers/net/ethernet/microchip/encx24j600_hw.h new file mode 100644 index 0000000..4be73d5 --- /dev/null +++ b/drivers/net/ethernet/microchip/encx24j600_hw.h @@ -0,0 +1,437 @@ +/** + * encx24j600_hw.h: Register definitions + * + */ + +#ifndef _ENCX24J600_HW_H +#define _ENCX24J600_HW_H + +struct encx24j600_context { + struct spi_device *spi; + struct regmap *regmap; + struct regmap *phymap; + struct mutex mutex; /* mutex to protect access to regmap */ + int bank; +}; + +void devm_regmap_init_encx24j600(struct device *dev, + struct encx24j600_context *ctx); + +/* Single-byte instructions */ +#define BANK_SELECT(bank) (0xC0 | ((bank & (BANK_MASK >> BANK_SHIFT)) << 1)) +#define B0SEL 0xC0 /* Bank 0 Select */ +#define B1SEL 0xC2 /* Bank 1 Select */ +#define B2SEL 0xC4 /* Bank 2 Select */ +#define B3SEL 0xC6 /* Bank 3 Select */ +#define SETETHRST 0xCA /* System Reset */ +#define FCDISABLE 0xE0 /* Flow Control Disable */ +#define FCSINGLE 0xE2 /* Flow Control Single */ +#define FCMULTIPLE 0xE4 /* Flow Control Multiple */ +#define FCCLEAR 0xE6 /* Flow Control Clear */ +#define SETPKTDEC 0xCC /* Decrement Packet Counter */ +#define DMASTOP 0xD2 /* DMA Stop */ +#define DMACKSUM 0xD8 /* DMA Start Checksum */ +#define DMACKSUMS 0xDA /* DMA Start Checksum with Seed */ +#define DMACOPY 0xDC /* DMA Start Copy */ +#define DMACOPYS 0xDE /* DMA Start Copy and Checksum with Seed */ +#define SETTXRTS 0xD4 /* Request Packet Transmission */ +#define ENABLERX 0xE8 /* Enable RX */ +#define DISABLERX 0xEA /* Disable RX */ +#define SETEIE 0xEC /* Enable Interrupts */ +#define CLREIE 0xEE /* Disable Interrupts */ + +/* Two byte instructions */ +#define RBSEL 0xC8 /* Read Bank Select */ + +/* Three byte instructions */ +#define WGPRDPT 0x60 /* Write EGPRDPT */ +#define RGPRDPT 0x62 /* Read EGPRDPT */ +#define WRXRDPT 0x64 /* Write ERXRDPT */ +#define RRXRDPT 0x66 /* Read ERXRDPT */ +#define WUDARDPT 0x68 /* Write EUDARDPT */ +#define RUDARDPT 0x6A /* Read EUDARDPT */ +#define WGPWRPT 0x6C /* Write EGPWRPT */ +#define RGPWRPT 0x6E /* Read EGPWRPT */ +#define WRXWRPT 0x70 /* Write ERXWRPT */ +#define RRXWRPT 0x72 /* Read ERXWRPT */ +#define WUDAWRPT 0x74 /* Write EUDAWRPT */ +#define RUDAWRPT 0x76 /* Read EUDAWRPT */ + +/* n byte instructions */ +#define RCRCODE 0x00 +#define WCRCODE 0x40 +#define BFSCODE 0x80 +#define BFCCODE 0xA0 +#define RCR(addr) (RCRCODE | (addr & ADDR_MASK)) /* Read Control Register */ +#define WCR(addr) (WCRCODE | (addr & ADDR_MASK)) /* Write Control Register */ +#define RCRU 0x20 /* Read Control Register Unbanked */ +#define WCRU 0x22 /* Write Control Register Unbanked */ +#define BFS(addr) (BFSCODE | (addr & ADDR_MASK)) /* Bit Field Set */ +#define BFC(addr) (BFCCODE | (addr & ADDR_MASK)) /* Bit Field Clear */ +#define BFSU 0x24 /* Bit Field Set Unbanked */ +#define BFCU 0x26 /* Bit Field Clear Unbanked */ +#define RGPDATA 0x28 /* Read EGPDATA */ +#define WGPDATA 0x2A /* Write EGPDATA */ +#define RRXDATA 0x2C /* Read ERXDATA */ +#define WRXDATA 0x2E /* Write ERXDATA */ +#define RUDADATA 0x30 /* Read EUDADATA */ +#define WUDADATA 0x32 /* Write EUDADATA */ + +#define SFR_REG_COUNT 0xA0 + +/* ENC424J600 Control Registers + * Control register definitions are a combination of address + * and bank number + * - Register address (bits 0-4) + * - Bank number (bits 5-6) + */ +#define ADDR_MASK 0x1F +#define BANK_MASK 0x60 +#define BANK_SHIFT 5 + +/* All-bank registers */ +#define EUDAST 0x16 +#define EUDAND 0x18 +#define ESTAT 0x1A +#define EIR 0x1C +#define ECON1 0x1E + +/* Bank 0 registers */ +#define ETXST (0x00 | 0x00) +#define ETXLEN (0x02 | 0x00) +#define ERXST (0x04 | 0x00) +#define ERXTAIL (0x06 | 0x00) +#define ERXHEAD (0x08 | 0x00) +#define EDMAST (0x0A | 0x00) +#define EDMALEN (0x0C | 0x00) +#define EDMADST (0x0E | 0x00) +#define EDMACS (0x10 | 0x00) +#define ETXSTAT (0x12 | 0x00) +#define ETXWIRE (0x14 | 0x00) + +/* Bank 1 registers */ +#define EHT1 (0x00 | 0x20) +#define EHT2 (0x02 | 0x20) +#define EHT3 (0x04 | 0x20) +#define EHT4 (0x06 | 0x20) +#define EPMM1 (0x08 | 0x20) +#define EPMM2 (0x0A | 0x20) +#define EPMM3 (0x0C | 0x20) +#define EPMM4 (0x0E | 0x20) +#define EPMCS (0x10 | 0x20) +#define EPMO (0x12 | 0x20) +#define ERXFCON (0x14 | 0x20) + +/* Bank 2 registers */ +#define MACON1 (0x00 | 0x40) +#define MACON2 (0x02 | 0x40) +#define MABBIPG (0x04 | 0x40) +#define MAIPG (0x06 | 0x40) +#define MACLCON (0x08 | 0x40) +#define MAMXFL (0x0A | 0x40) +#define MICMD (0x12 | 0x40) +#define MIREGADR (0x14 | 0x40) + +/* Bank 3 registers */ +#define MAADR3 (0x00 | 0x60) +#define MAADR2 (0x02 | 0x60) +#define MAADR1 (0x04 | 0x60) +#define MIWR (0x06 | 0x60) +#define MIRD (0x08 | 0x60) +#define MISTAT (0x0A | 0x60) +#define EPAUS (0x0C | 0x60) +#define ECON2 (0x0E | 0x60) +#define ERXWM (0x10 | 0x60) +#define EIE (0x12 | 0x60) +#define EIDLED (0x14 | 0x60) + +/* Unbanked registers */ +#define EGPDATA (0x00 | 0x80) +#define ERXDATA (0x02 | 0x80) +#define EUDADATA (0x04 | 0x80) +#define EGPRDPT (0x06 | 0x80) +#define EGPWRPT (0x08 | 0x80) +#define ERXRDPT (0x0A | 0x80) +#define ERXWRPT (0x0C | 0x80) +#define EUDARDPT (0x0E | 0x80) +#define EUDAWRPT (0x10 | 0x80) + + +/* Register bit definitions */ +/* ESTAT */ +#define INT (1 << 15) +#define FCIDLE (1 << 14) +#define RXBUSY (1 << 13) +#define CLKRDY (1 << 12) +#define PHYDPX (1 << 10) +#define PHYLNK (1 << 8) + +/* EIR */ +#define CRYPTEN (1 << 15) +#define MODEXIF (1 << 14) +#define HASHIF (1 << 13) +#define AESIF (1 << 12) +#define LINKIF (1 << 11) +#define PKTIF (1 << 6) +#define DMAIF (1 << 5) +#define TXIF (1 << 3) +#define TXABTIF (1 << 2) +#define RXABTIF (1 << 1) +#define PCFULIF (1 << 0) + +/* ECON1 */ +#define MODEXST (1 << 15) +#define HASHEN (1 << 14) +#define HASHOP (1 << 13) +#define HASHLST (1 << 12) +#define AESST (1 << 11) +#define AESOP1 (1 << 10) +#define AESOP0 (1 << 9) +#define PKTDEC (1 << 8) +#define FCOP1 (1 << 7) +#define FCOP0 (1 << 6) +#define DMAST (1 << 5) +#define DMACPY (1 << 4) +#define DMACSSD (1 << 3) +#define DMANOCS (1 << 2) +#define TXRTS (1 << 1) +#define RXEN (1 << 0) + +/* ETXSTAT */ +#define LATECOL (1 << 10) +#define MAXCOL (1 << 9) +#define EXDEFER (1 << 8) +#define ETXSTATL_DEFER (1 << 7) +#define CRCBAD (1 << 4) +#define COLCNT_MASK 0xF + +/* ERXFCON */ +#define HTEN (1 << 15) +#define MPEN (1 << 14) +#define NOTPM (1 << 12) +#define PMEN3 (1 << 11) +#define PMEN2 (1 << 10) +#define PMEN1 (1 << 9) +#define PMEN0 (1 << 8) +#define CRCEEN (1 << 7) +#define CRCEN (1 << 6) +#define RUNTEEN (1 << 5) +#define RUNTEN (1 << 4) +#define UCEN (1 << 3) +#define NOTMEEN (1 << 2) +#define MCEN (1 << 1) +#define BCEN (1 << 0) + +/* MACON1 */ +#define LOOPBK (1 << 4) +#define RXPAUS (1 << 2) +#define PASSALL (1 << 1) + +/* MACON2 */ +#define MACON2_DEFER (1 << 14) +#define BPEN (1 << 13) +#define NOBKOFF (1 << 12) +#define PADCFG2 (1 << 7) +#define PADCFG1 (1 << 6) +#define PADCFG0 (1 << 5) +#define TXCRCEN (1 << 4) +#define PHDREN (1 << 3) +#define HFRMEN (1 << 2) +#define MACON2_RSV1 (1 << 1) +#define FULDPX (1 << 0) + +/* MAIPG */ +/* value of the high byte is given by the reserved bits, + * value of the low byte is recomended setting of the + * IPG parameter. + */ +#define MAIPGH_VAL 0x0C +#define MAIPGL_VAL 0x12 + +/* MIREGADRH */ +#define MIREGADR_VAL (1 << 8) + +/* MIREGADRL */ +#define PHREG_MASK 0x1F + +/* MICMD */ +#define MIISCAN (1 << 1) +#define MIIRD (1 << 0) + +/* MISTAT */ +#define NVALID (1 << 2) +#define SCAN (1 << 1) +#define BUSY (1 << 0) + +/* ECON2 */ +#define ETHEN (1 << 15) +#define STRCH (1 << 14) +#define TXMAC (1 << 13) +#define SHA1MD5 (1 << 12) +#define COCON3 (1 << 11) +#define COCON2 (1 << 10) +#define COCON1 (1 << 9) +#define COCON0 (1 << 8) +#define AUTOFC (1 << 7) +#define TXRST (1 << 6) +#define RXRST (1 << 5) +#define ETHRST (1 << 4) +#define MODLEN1 (1 << 3) +#define MODLEN0 (1 << 2) +#define AESLEN1 (1 << 1) +#define AESLEN0 (1 << 0) + +/* EIE */ +#define INTIE (1 << 15) +#define MODEXIE (1 << 14) +#define HASHIE (1 << 13) +#define AESIE (1 << 12) +#define LINKIE (1 << 11) +#define PKTIE (1 << 6) +#define DMAIE (1 << 5) +#define TXIE (1 << 3) +#define TXABTIE (1 << 2) +#define RXABTIE (1 << 1) +#define PCFULIE (1 << 0) + +/* EIDLED */ +#define LACFG3 (1 << 15) +#define LACFG2 (1 << 14) +#define LACFG1 (1 << 13) +#define LACFG0 (1 << 12) +#define LBCFG3 (1 << 11) +#define LBCFG2 (1 << 10) +#define LBCFG1 (1 << 9) +#define LBCFG0 (1 << 8) +#define DEVID_SHIFT 5 +#define DEVID_MASK (0x7 << DEVID_SHIFT) +#define REVID_SHIFT 0 +#define REVID_MASK (0x1F << REVID_SHIFT) + +/* PHY registers */ +#define PHCON1 0x00 +#define PHSTAT1 0x01 +#define PHANA 0x04 +#define PHANLPA 0x05 +#define PHANE 0x06 +#define PHCON2 0x11 +#define PHSTAT2 0x1B +#define PHSTAT3 0x1F + +/* PHCON1 */ +#define PRST (1 << 15) +#define PLOOPBK (1 << 14) +#define SPD100 (1 << 13) +#define ANEN (1 << 12) +#define PSLEEP (1 << 11) +#define RENEG (1 << 9) +#define PFULDPX (1 << 8) + +/* PHSTAT1 */ +#define FULL100 (1 << 14) +#define HALF100 (1 << 13) +#define FULL10 (1 << 12) +#define HALF10 (1 << 11) +#define ANDONE (1 << 5) +#define LRFAULT (1 << 4) +#define ANABLE (1 << 3) +#define LLSTAT (1 << 2) +#define EXTREGS (1 << 0) + +/* PHSTAT2 */ +#define PLRITY (1 << 4) + +/* PHSTAT3 */ +#define PHY3SPD100 (1 << 3) +#define PHY3DPX (1 << 4) +#define SPDDPX_SHIFT 2 +#define SPDDPX_MASK (0x7 << SPDDPX_SHIFT) + +/* PHANA */ +/* Default value for PHY initialization*/ +#define PHANA_DEFAULT 0x05E1 + +/* PHANE */ +#define PDFLT (1 << 4) +#define LPARCD (1 << 1) +#define LPANABL (1 << 0) + +#define EUDAST_TEST_VAL 0x1234 + +#define TSV_SIZE 7 + +#define ENCX24J600_DEV_ID 0x1 + +/* Configuration */ + +/* Led is on when the link is present and driven low + * temporarily when packet is TX'd or RX'd + */ +#define LED_A_SETTINGS 0xC + +/* Led is on if the link is in 100 Mbps mode */ +#define LED_B_SETTINGS 0x8 + +/* maximum ethernet frame length + * Currently not used as a limit anywhere + * (we're using the "huge frame enable" feature of + * enc424j600). + */ +#define MAX_FRAMELEN 1518 + +/* Size in bytes of the receive buffer in enc424j600. + * Must be word aligned (even). + */ +#define RX_BUFFER_SIZE (15 * MAX_FRAMELEN) + +/* Start of the general purpose area in sram */ +#define SRAM_GP_START 0x0 + +/* SRAM size */ +#define SRAM_SIZE 0x6000 + +/* Start of the receive buffer */ +#define ERXST_VAL (SRAM_SIZE - RX_BUFFER_SIZE) + +#define RSV_RXLONGEVDROPEV 16 +#define RSV_CARRIEREV 18 +#define RSV_CRCERROR 20 +#define RSV_LENCHECKERR 21 +#define RSV_LENOUTOFRANGE 22 +#define RSV_RXOK 23 +#define RSV_RXMULTICAST 24 +#define RSV_RXBROADCAST 25 +#define RSV_DRIBBLENIBBLE 26 +#define RSV_RXCONTROLFRAME 27 +#define RSV_RXPAUSEFRAME 28 +#define RSV_RXUNKNOWNOPCODE 29 +#define RSV_RXTYPEVLAN 30 + +#define RSV_RUNTFILTERMATCH 31 +#define RSV_NOTMEFILTERMATCH 32 +#define RSV_HASHFILTERMATCH 33 +#define RSV_MAGICPKTFILTERMATCH 34 +#define RSV_PTRNMTCHFILTERMATCH 35 +#define RSV_UNICASTFILTERMATCH 36 + +#define RSV_SIZE 8 +#define RSV_BITMASK(x) (1 << ((x) - 16)) +#define RSV_GETBIT(x, y) (((x) & RSV_BITMASK(y)) ? 1 : 0) + +struct rsv { + u16 next_packet; + u16 len; + u32 rxstat; +}; + +/* Put RX buffer at 0 as suggested by the Errata datasheet */ + +#define RXSTART_INIT ERXST_VAL +#define RXEND_INIT 0x5FFF + +int regmap_encx24j600_spi_write(void *context, u8 reg, const u8 *data, + size_t count); +int regmap_encx24j600_spi_read(void *context, u8 reg, u8 *data, size_t count); + + +#endif -- cgit v0.10.2 From 9886ce2b9d4e5a8bb3d78d0f7eff3c0f1ed58d67 Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Thu, 1 Oct 2015 20:26:20 +0800 Subject: net: encx24j600_exit() can be static Signed-off-by: Fengguang Wu Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/microchip/encx24j600.c b/drivers/net/ethernet/microchip/encx24j600.c index 1da37fd..afc008a 100644 --- a/drivers/net/ethernet/microchip/encx24j600.c +++ b/drivers/net/ethernet/microchip/encx24j600.c @@ -1112,7 +1112,7 @@ static int __init encx24j600_init(void) } module_init(encx24j600_init); -void encx24j600_exit(void) +static void encx24j600_exit(void) { spi_unregister_driver(&encx24j600_spi_net_driver); } -- cgit v0.10.2 From 3e0fa50575dbc83e57ceee7c413ff9b4e09a9a80 Mon Sep 17 00:00:00 2001 From: Assaf Krauss Date: Wed, 16 Sep 2015 11:44:55 +0300 Subject: iwlwifi: mvm: Fix tof debugfs formats (dec vs. hex) Make some input formats more natural, e.g. bandwidth and periods are more natural in decimal than in hexadecimal. Signed-off-by: Assaf Krauss Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c index 334ae56..15ab543 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c @@ -913,11 +913,11 @@ static ssize_t iwl_dbgfs_tof_range_request_write(struct ieee80211_vif *vif, u8 *mac = ap.bssid; unsigned int i; - if (sscanf(data, "%u %hhd %hhx %hhx" + if (sscanf(data, "%u %hhd %hhd %hhd" "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx" - "%hhx %hhx %hx" - "%hhx %hhx %x" - "%hhx %hhx %hhx %hhx", + "%hhd %hhd %hd" + "%hhd %hhd %d" + "%hhx %hhd %hhd %hhd", &i, &ap.channel_num, &ap.bandwidth, &ap.ctrl_ch_position, mac, mac + 1, mac + 2, mac + 3, mac + 4, mac + 5, @@ -994,16 +994,18 @@ static ssize_t iwl_dbgfs_tof_range_request_read(struct file *file, struct iwl_tof_range_req_ap_entry *ap = &cmd->ap[i]; pos += scnprintf(buf + pos, bufsz - pos, - "ap %.2d: channel_num=%hhx bw=%hhx" - " control=%hhx bssid=%pM type=%hhx" - " num_of_bursts=%hhx burst_period=%hx ftm=%hhx" - " retries=%hhx tsf_delta=%x location_req=%hhx " - " asap=%hhx enable=%hhx rssi=%hhx\n", + "ap %.2d: channel_num=%hhd bw=%hhd" + " control=%hhd bssid=%pM type=%hhd" + " num_of_bursts=%hhd burst_period=%hd ftm=%hhd" + " retries=%hhd tsf_delta=%d" + " tsf_delta_direction=%hhd location_req=0x%hhx " + " asap=%hhd enable=%hhd rssi=%hhd\n", i, ap->channel_num, ap->bandwidth, ap->ctrl_ch_position, ap->bssid, ap->measure_type, ap->num_of_bursts, ap->burst_period, ap->samples_per_burst, ap->retries_per_sample, ap->tsf_delta, + ap->tsf_delta_direction, ap->location_req, ap->asap_mode, ap->enable_dyn_ack, ap->rssi); } @@ -1099,18 +1101,18 @@ static ssize_t iwl_dbgfs_tof_range_req_ext_read(struct file *file, mutex_lock(&mvm->mutex); pos += scnprintf(buf + pos, bufsz - pos, - "tsf_timer_offset_msec = %hx\n", + "tsf_timer_offset_msec = %hd\n", cmd->tsf_timer_offset_msec); - pos += scnprintf(buf + pos, bufsz - pos, "min_delta_ftm = %hhx\n", + pos += scnprintf(buf + pos, bufsz - pos, "min_delta_ftm = %hhd\n", cmd->min_delta_ftm); pos += scnprintf(buf + pos, bufsz - pos, - "ftm_format_and_bw20M = %hhx\n", + "ftm_format_and_bw20M = %hhd\n", cmd->ftm_format_and_bw20M); pos += scnprintf(buf + pos, bufsz - pos, - "ftm_format_and_bw40M = %hhx\n", + "ftm_format_and_bw40M = %hhd\n", cmd->ftm_format_and_bw40M); pos += scnprintf(buf + pos, bufsz - pos, - "ftm_format_and_bw80M = %hhx\n", + "ftm_format_and_bw80M = %hhd\n", cmd->ftm_format_and_bw80M); mutex_unlock(&mvm->mutex); @@ -1205,11 +1207,11 @@ static ssize_t iwl_dbgfs_tof_range_response_read(struct file *file, struct iwl_tof_range_rsp_ap_entry_ntfy *ap = &cmd->ap[i]; pos += scnprintf(buf + pos, bufsz - pos, - "ap %.2d: bssid=%pM status=%hhx bw=%hhx" - " rtt=%x rtt_var=%x rtt_spread=%x" - " rssi=%hhx rssi_spread=%hhx" - " range=%x range_var=%x" - " time_stamp=%x\n", + "ap %.2d: bssid=%pM status=%hhd bw=%hhd" + " rtt=%d rtt_var=%d rtt_spread=%d" + " rssi=%hhd rssi_spread=%hhd" + " range=%d range_var=%d" + " time_stamp=%d\n", i, ap->bssid, ap->measure_status, ap->measure_bw, ap->rtt, ap->rtt_variance, ap->rtt_spread, -- cgit v0.10.2 From 9ef2b8befb5acf499ac60e2a18dfa3e41194ba6e Mon Sep 17 00:00:00 2001 From: Gregory Greenman Date: Mon, 20 Jul 2015 07:44:44 +0300 Subject: iwlwifi: mvm: ToF - fill bssid of responder configuration The command needs to have the AP interfaces BSSID (which corresponds to its address). Signed-off-by: Gregory Greenman Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/tof.c b/drivers/net/wireless/iwlwifi/mvm/tof.c index 7ae0bd8..4007f1d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tof.c +++ b/drivers/net/wireless/iwlwifi/mvm/tof.c @@ -185,6 +185,7 @@ int iwl_mvm_tof_responder_cmd(struct iwl_mvm *mvm, } cmd->sta_id = mvmvif->bcast_sta.sta_id; + memcpy(cmd->bssid, vif->addr, ETH_ALEN); return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD, IWL_ALWAYS_LONG_GROUP, 0), 0, sizeof(*cmd), cmd); -- cgit v0.10.2 From 5ac15be8fa5193206f5c47172a63cf83bec91f66 Mon Sep 17 00:00:00 2001 From: Assaf Krauss Date: Wed, 16 Sep 2015 11:47:14 +0300 Subject: iwlwifi: mvm: Improve debugfs tof robustness Return a proper error when wrong parameters are passed to debugfs tof_range_request. Signed-off-by: Assaf Krauss Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c index 15ab543..5dc1686 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c @@ -892,6 +892,7 @@ static ssize_t iwl_dbgfs_tof_range_request_write(struct ieee80211_vif *vif, goto out; } memcpy(mvm->tof_data.range_req.macaddr_template, mac, ETH_ALEN); + goto out; } data = iwl_dbgfs_is_match("macaddr_mask=", buf); @@ -903,11 +904,12 @@ static ssize_t iwl_dbgfs_tof_range_request_write(struct ieee80211_vif *vif, goto out; } memcpy(mvm->tof_data.range_req.macaddr_mask, mac, ETH_ALEN); + goto out; } data = iwl_dbgfs_is_match("ap=", buf); if (data) { - struct iwl_tof_range_req_ap_entry ap; + struct iwl_tof_range_req_ap_entry ap = {}; int size = sizeof(struct iwl_tof_range_req_ap_entry); u16 burst_period; u8 *mac = ap.bssid; @@ -944,12 +946,12 @@ static ssize_t iwl_dbgfs_tof_range_request_write(struct ieee80211_vif *vif, data = iwl_dbgfs_is_match("send_range_request=", buf); if (data) { ret = kstrtou32(data, 10, &value); - if (ret == 0 && value) { + if (ret == 0 && value) ret = iwl_mvm_tof_range_request_cmd(mvm, vif); - goto out; - } + goto out; } + ret = -EINVAL; out: mutex_unlock(&mvm->mutex); return ret ?: count; @@ -1073,12 +1075,12 @@ static ssize_t iwl_dbgfs_tof_range_req_ext_write(struct ieee80211_vif *vif, data = iwl_dbgfs_is_match("send_range_req_ext=", buf); if (data) { ret = kstrtou32(data, 10, &value); - if (ret == 0 && value) { + if (ret == 0 && value) ret = iwl_mvm_tof_range_request_ext_cmd(mvm, vif); - goto out; - } + goto out; } + ret = -EINVAL; out: mutex_unlock(&mvm->mutex); return ret ?: count; -- cgit v0.10.2 From 1939089300cb4709dec2b268db649adfdefdb7bc Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 3 Sep 2015 14:53:09 +0200 Subject: iwlwifi: mvm: remove PHY RX from handlers Treat PHY RX specially, since it's actually pretty frequent, doesn't need all the notication etc. code, and will have a different handler in future hardware. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 23825ff..1c38e45 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -222,7 +222,6 @@ struct iwl_rx_handlers { * called from a worker with mvm->mutex held. */ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { - RX_HANDLER(REPLY_RX_PHY_CMD, iwl_mvm_rx_rx_phy_cmd, false), RX_HANDLER(TX_CMD, iwl_mvm_rx_tx_cmd, false), RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, false), @@ -730,6 +729,9 @@ static void iwl_mvm_rx_dispatch(struct iwl_op_mode *op_mode, if (likely(pkt->hdr.cmd == REPLY_RX_MPDU_CMD)) { iwl_mvm_rx_rx_mpdu(mvm, napi, rxb); return; + } else if (pkt->hdr.cmd == REPLY_RX_PHY_CMD) { + iwl_mvm_rx_rx_phy_cmd(mvm, rxb); + return; } iwl_mvm_rx_check_trigger(mvm, pkt); -- cgit v0.10.2 From a190430c5eeaff1b3a0cfc05d3572650b3bd3beb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 22 May 2015 10:52:26 +0200 Subject: iwlwifi: op-mode API: add rx_rss method Upcoming hardware will have the ability to do L3 hashing for RSS, directing data packets (and perhaps some associated metadata and management notifications) to different MSI-X vectors. In this case, it makes no sense to go through the full RX dispatch since it's already known that only a subset of the possibilities can come in, requiring a new receive method. In addition this must know which queue the packet was received on. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/iwlwifi/iwl-op-mode.h index b47fe9d..2a58d68 100644 --- a/drivers/net/wireless/iwlwifi/iwl-op-mode.h +++ b/drivers/net/wireless/iwlwifi/iwl-op-mode.h @@ -7,6 +7,7 @@ * * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH * * 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 @@ -33,6 +34,7 @@ * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -108,7 +110,8 @@ struct iwl_cfg; * interact with it. The driver layer typically calls the start and stop * handlers, the transport layer calls the others. * - * All the handlers MUST be implemented + * All the handlers MUST be implemented, except @rx_rss which can be left + * out *iff* the opmode will never run on hardware with multi-queue capability. * * @start: start the op_mode. The transport layer is already allocated. * May sleep @@ -116,6 +119,10 @@ struct iwl_cfg; * May sleep * @rx: Rx notification to the op_mode. rxb is the Rx buffer itself. Cmd is the * HCMD this Rx responds to. Can't sleep. + * @rx_rss: data queue RX notification to the op_mode, for (data) notifications + * received on the RSS queue(s). The queue parameter indicates which of the + * RSS queues received this frame; it will always be non-zero. + * This method must not sleep. * @queue_full: notifies that a HW queue is full. * Must be atomic and called with BH disabled. * @queue_not_full: notifies that a HW queue is not full any more. @@ -146,6 +153,8 @@ struct iwl_op_mode_ops { void (*stop)(struct iwl_op_mode *op_mode); void (*rx)(struct iwl_op_mode *op_mode, struct napi_struct *napi, struct iwl_rx_cmd_buffer *rxb); + void (*rx_rss)(struct iwl_op_mode *op_mode, struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb, unsigned int queue); void (*queue_full)(struct iwl_op_mode *op_mode, int queue); void (*queue_not_full)(struct iwl_op_mode *op_mode, int queue); bool (*hw_rf_kill)(struct iwl_op_mode *op_mode, bool state); @@ -186,6 +195,14 @@ static inline void iwl_op_mode_rx(struct iwl_op_mode *op_mode, return op_mode->ops->rx(op_mode, napi, rxb); } +static inline void iwl_op_mode_rx_rss(struct iwl_op_mode *op_mode, + struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb, + unsigned int queue) +{ + op_mode->ops->rx_rss(op_mode, napi, rxb, queue); +} + static inline void iwl_op_mode_queue_full(struct iwl_op_mode *op_mode, int queue) { -- cgit v0.10.2 From 56882e6cab432508c79b350d7842af3abac9c7d3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 22 May 2015 11:33:12 +0200 Subject: iwlwifi: transport: track number of allocated queues As the transport will decide how many queues (and MSI-X vectors) to allocate, add a field to indicate that to the op-mode so it can size/allocate its own data structures appropriately. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.c b/drivers/net/wireless/iwlwifi/iwl-trans.c index 9f8bcef..7161096 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans.c @@ -87,6 +87,7 @@ struct iwl_trans *iwl_trans_alloc(unsigned int priv_size, trans->cfg = cfg; trans->ops = ops; trans->dev_cmd_headroom = dev_cmd_headroom; + trans->num_rx_queues = 1; snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name), "iwl_cmd_pool:%s", dev_name(trans->dev)); diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index c829c50..bb51b6f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -386,6 +386,7 @@ static inline void iwl_free_rxb(struct iwl_rx_cmd_buffer *r) #define IWL_MAX_HW_QUEUES 32 #define IWL_MAX_TID_COUNT 8 #define IWL_FRAME_LIMIT 64 +#define IWL_MAX_RX_HW_QUEUES 16 /** * enum iwl_wowlan_status - WoWLAN image/device status @@ -654,6 +655,8 @@ enum iwl_d0i3_mode { * @hw_id_str: a string with info about HW ID. Set during transport allocation. * @pm_support: set to true in start_hw if link pm is supported * @ltr_enabled: set to true if the LTR is enabled + * @num_rx_queues: number of RX queues allocated by the transport; + * the transport must set this before calling iwl_drv_start() * @dev_cmd_pool: pool for Tx cmd allocation - for internal use only. * The user should use iwl_trans_{alloc,free}_tx_cmd. * @dev_cmd_headroom: room needed for the transport's private use before the @@ -693,6 +696,8 @@ struct iwl_trans { bool pm_support; bool ltr_enabled; + u8 num_rx_queues; + /* The following fields are internal only */ struct kmem_cache *dev_cmd_pool; size_t dev_cmd_headroom; -- cgit v0.10.2 From 4ecafae9e56802575c3b0c45ecf3dedecd3fd9e3 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Tue, 14 Jul 2015 13:36:18 +0300 Subject: iwlwifi: mvm: support using multiple ACs on single HW queue "DQA" is shorthand for "dynamic queue allocation", with the idea of allocating queues per-RA/TID on-demand rather than using shared queues statically allocated per vif. The goal of this is to enable future features (like GO PM) and to improve performance measurements of TX traffic. When RA/TID streams can't be neatly sorted into different AC queues, DQA allows sharing queues for the same RA. This means that DQA allows different ACs may reach the same HW queue. Update the code to allow such queue sharing by having a mapping between the HW queue and the mac80211 queues using it (as this could be more than one queue). Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c index 248b025..834641e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/iwlwifi/mvm/fw.c @@ -616,12 +616,8 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, * will be empty. */ - for (i = 0; i < IWL_MAX_HW_QUEUES; i++) { - if (i < mvm->first_agg_queue && i != IWL_MVM_CMD_QUEUE) - mvm->queue_to_mac80211[i] = i; - else - mvm->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE; - } + memset(&mvm->queue_info, 0, sizeof(mvm->queue_info)); + mvm->queue_info[IWL_MVM_CMD_QUEUE].hw_queue_refcount = 1; for (i = 0; i < IEEE80211_MAX_QUEUES; i++) atomic_set(&mvm->mac80211_queue_stop_count[i], 0); diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index 3615650..d2a221d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -486,15 +486,17 @@ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif) switch (vif->type) { case NL80211_IFTYPE_P2P_DEVICE: iwl_mvm_enable_ac_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE, + IWL_MVM_OFFCHANNEL_QUEUE, IWL_MVM_TX_FIFO_VO, wdg_timeout); break; case NL80211_IFTYPE_AP: - iwl_mvm_enable_ac_txq(mvm, vif->cab_queue, + iwl_mvm_enable_ac_txq(mvm, vif->cab_queue, vif->cab_queue, IWL_MVM_TX_FIFO_MCAST, wdg_timeout); /* fall through */ default: for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) iwl_mvm_enable_ac_txq(mvm, vif->hw_queue[ac], + vif->hw_queue[ac], iwl_mvm_ac_to_tx_fifo[ac], wdg_timeout); break; @@ -511,14 +513,19 @@ void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif) switch (vif->type) { case NL80211_IFTYPE_P2P_DEVICE: - iwl_mvm_disable_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE, 0); + iwl_mvm_disable_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE, + IWL_MVM_OFFCHANNEL_QUEUE, IWL_MAX_TID_COUNT, + 0); break; case NL80211_IFTYPE_AP: - iwl_mvm_disable_txq(mvm, vif->cab_queue, 0); + iwl_mvm_disable_txq(mvm, vif->cab_queue, vif->cab_queue, + IWL_MAX_TID_COUNT, 0); /* fall through */ default: for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) - iwl_mvm_disable_txq(mvm, vif->hw_queue[ac], 0); + iwl_mvm_disable_txq(mvm, vif->hw_queue[ac], + vif->hw_queue[ac], + IWL_MAX_TID_COUNT, 0); } } diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 5e02736..a7a9619 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -82,7 +82,6 @@ #include "constants.h" #include "tof.h" -#define IWL_INVALID_MAC80211_QUEUE 0xff #define IWL_MVM_MAX_ADDRESSES 5 /* RSSI offset for WkP */ #define IWL_RSSI_OFFSET 50 @@ -605,7 +604,14 @@ struct iwl_mvm { u64 on_time_scan; } radio_stats, accu_radio_stats; - u8 queue_to_mac80211[IWL_MAX_HW_QUEUES]; + struct { + /* Map to HW queue */ + u32 hw_queue_to_mac80211; + u8 hw_queue_refcount; + bool setup_reserved; + u16 tid_bitmap; /* Bitmap of the TIDs mapped to this queue */ + } queue_info[IWL_MAX_HW_QUEUES]; + spinlock_t queue_info_lock; /* For syncing queue mgmt operations */ atomic_t mac80211_queue_stop_count[IEEE80211_MAX_QUEUES]; const char *nvm_file_name; @@ -910,6 +916,12 @@ static inline bool iwl_mvm_is_d0i3_supported(struct iwl_mvm *mvm) IWL_UCODE_TLV_CAPA_D0I3_SUPPORT); } +static inline bool iwl_mvm_is_dqa_supported(struct iwl_mvm *mvm) +{ + return fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_DQA_SUPPORT); +} + static inline bool iwl_mvm_is_lar_supported(struct iwl_mvm *mvm) { bool nvm_lar = mvm->nvm_data->lar_enabled; @@ -1341,13 +1353,19 @@ static inline bool iwl_mvm_vif_low_latency(struct iwl_mvm_vif *mvmvif) } /* hw scheduler queue config */ -void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, u16 ssn, - const struct iwl_trans_txq_scd_cfg *cfg, +void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, + u16 ssn, const struct iwl_trans_txq_scd_cfg *cfg, unsigned int wdg_timeout); -void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, u8 flags); +/* + * Disable a TXQ. + * Note that in non-DQA mode the %mac80211_queue and %tid params are ignored. + */ +void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, + u8 tid, u8 flags); +int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 minq, u8 maxq); static inline -void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue, +void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, u8 fifo, unsigned int wdg_timeout) { struct iwl_trans_txq_scd_cfg cfg = { @@ -1357,13 +1375,13 @@ void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue, .frame_limit = IWL_FRAME_LIMIT, }; - iwl_mvm_enable_txq(mvm, queue, 0, &cfg, wdg_timeout); + iwl_mvm_enable_txq(mvm, queue, mac80211_queue, 0, &cfg, wdg_timeout); } static inline void iwl_mvm_enable_agg_txq(struct iwl_mvm *mvm, int queue, - int fifo, int sta_id, int tid, - int frame_limit, u16 ssn, - unsigned int wdg_timeout) + int mac80211_queue, int fifo, + int sta_id, int tid, int frame_limit, + u16 ssn, unsigned int wdg_timeout) { struct iwl_trans_txq_scd_cfg cfg = { .fifo = fifo, @@ -1373,7 +1391,7 @@ static inline void iwl_mvm_enable_agg_txq(struct iwl_mvm *mvm, int queue, .aggregate = true, }; - iwl_mvm_enable_txq(mvm, queue, ssn, &cfg, wdg_timeout); + iwl_mvm_enable_txq(mvm, queue, mac80211_queue, ssn, &cfg, wdg_timeout); } /* Thermal management and CT-kill */ diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 1c38e45..0e8166f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -453,6 +453,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, INIT_LIST_HEAD(&mvm->aux_roc_te_list); INIT_LIST_HEAD(&mvm->async_handlers_list); spin_lock_init(&mvm->time_event_lock); + spin_lock_init(&mvm->queue_info_lock); INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk); INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk); @@ -775,37 +776,51 @@ static void iwl_mvm_rx_dispatch(struct iwl_op_mode *op_mode, static void iwl_mvm_stop_sw_queue(struct iwl_op_mode *op_mode, int queue) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - int mq = mvm->queue_to_mac80211[queue]; + unsigned long mq; + int q; - if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE)) - return; + spin_lock_bh(&mvm->queue_info_lock); + mq = mvm->queue_info[queue].hw_queue_to_mac80211; + spin_unlock_bh(&mvm->queue_info_lock); - if (atomic_inc_return(&mvm->mac80211_queue_stop_count[mq]) > 1) { - IWL_DEBUG_TX_QUEUES(mvm, - "queue %d (mac80211 %d) already stopped\n", - queue, mq); + if (WARN_ON_ONCE(!mq)) return; - } - ieee80211_stop_queue(mvm->hw, mq); + for_each_set_bit(q, &mq, IEEE80211_MAX_QUEUES) { + if (atomic_inc_return(&mvm->mac80211_queue_stop_count[q]) > 1) { + IWL_DEBUG_TX_QUEUES(mvm, + "queue %d (mac80211 %d) already stopped\n", + queue, q); + continue; + } + + ieee80211_stop_queue(mvm->hw, q); + } } static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int queue) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - int mq = mvm->queue_to_mac80211[queue]; + unsigned long mq; + int q; - if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE)) - return; + spin_lock_bh(&mvm->queue_info_lock); + mq = mvm->queue_info[queue].hw_queue_to_mac80211; + spin_unlock_bh(&mvm->queue_info_lock); - if (atomic_dec_return(&mvm->mac80211_queue_stop_count[mq]) > 0) { - IWL_DEBUG_TX_QUEUES(mvm, - "queue %d (mac80211 %d) still stopped\n", - queue, mq); + if (WARN_ON_ONCE(!mq)) return; - } - ieee80211_wake_queue(mvm->hw, mq); + for_each_set_bit(q, &mq, IEEE80211_MAX_QUEUES) { + if (atomic_dec_return(&mvm->mac80211_queue_stop_count[q]) > 0) { + IWL_DEBUG_TX_QUEUES(mvm, + "queue %d (mac80211 %d) still stopped\n", + queue, q); + continue; + } + + ieee80211_wake_queue(mvm->hw, q); + } } void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state) diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 4967990..9bda6c9 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -234,6 +234,7 @@ static int iwl_mvm_tdls_sta_init(struct iwl_mvm *mvm, /* Found a place for all queues - enable them */ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { iwl_mvm_enable_ac_txq(mvm, mvmsta->hw_queue[ac], + mvmsta->hw_queue[ac], iwl_mvm_ac_to_tx_fifo[ac], wdg_timeout); mvmsta->tfd_queue_msk |= BIT(mvmsta->hw_queue[ac]); } @@ -253,7 +254,7 @@ static void iwl_mvm_tdls_sta_deinit(struct iwl_mvm *mvm, /* disable the TDLS STA-specific queues */ sta_msk = mvmsta->tfd_queue_msk; for_each_set_bit(i, &sta_msk, sizeof(sta_msk) * BITS_PER_BYTE) - iwl_mvm_disable_txq(mvm, i, 0); + iwl_mvm_disable_txq(mvm, i, i, 0, 0); } int iwl_mvm_add_sta(struct iwl_mvm *mvm, @@ -472,7 +473,7 @@ void iwl_mvm_sta_drained_wk(struct work_struct *wk) unsigned long i, msk = mvm->tfd_drained[sta_id]; for_each_set_bit(i, &msk, sizeof(msk) * BITS_PER_BYTE) - iwl_mvm_disable_txq(mvm, i, 0); + iwl_mvm_disable_txq(mvm, i, i, 0, 0); mvm->tfd_drained[sta_id] = 0; IWL_DEBUG_TDLS(mvm, "Drained sta %d, with queues %ld\n", @@ -651,7 +652,7 @@ int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm) lockdep_assert_held(&mvm->mutex); /* Map Aux queue to fifo - needs to happen before adding Aux station */ - iwl_mvm_enable_ac_txq(mvm, mvm->aux_queue, + iwl_mvm_enable_ac_txq(mvm, mvm->aux_queue, mvm->aux_queue, IWL_MVM_TX_FIFO_MCAST, wdg_timeout); /* Allocate aux station and assign to it the aux queue */ @@ -923,6 +924,7 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_tid_data *tid_data; int txq_id; + int ret; if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) return -EINVAL; @@ -935,17 +937,6 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, lockdep_assert_held(&mvm->mutex); - for (txq_id = mvm->first_agg_queue; - txq_id <= mvm->last_agg_queue; txq_id++) - if (mvm->queue_to_mac80211[txq_id] == - IWL_INVALID_MAC80211_QUEUE) - break; - - if (txq_id > mvm->last_agg_queue) { - IWL_ERR(mvm, "Failed to allocate agg queue\n"); - return -EIO; - } - spin_lock_bh(&mvmsta->lock); /* possible race condition - we entered D0i3 while starting agg */ @@ -955,8 +946,18 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, return -EIO; } - /* the new tx queue is still connected to the same mac80211 queue */ - mvm->queue_to_mac80211[txq_id] = vif->hw_queue[tid_to_mac80211_ac[tid]]; + spin_lock_bh(&mvm->queue_info_lock); + + txq_id = iwl_mvm_find_free_queue(mvm, mvm->first_agg_queue, + mvm->last_agg_queue); + if (txq_id < 0) { + ret = txq_id; + spin_unlock_bh(&mvm->queue_info_lock); + IWL_ERR(mvm, "Failed to allocate agg queue\n"); + goto release_locks; + } + mvm->queue_info[txq_id].setup_reserved = true; + spin_unlock_bh(&mvm->queue_info_lock); tid_data = &mvmsta->tid_data[tid]; tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); @@ -975,9 +976,12 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, tid_data->state = IWL_EMPTYING_HW_QUEUE_ADDBA; } + ret = 0; + +release_locks: spin_unlock_bh(&mvmsta->lock); - return 0; + return ret; } int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, @@ -1005,13 +1009,19 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]]; - iwl_mvm_enable_agg_txq(mvm, queue, fifo, mvmsta->sta_id, tid, - buf_size, ssn, wdg_timeout); + iwl_mvm_enable_agg_txq(mvm, queue, + vif->hw_queue[tid_to_mac80211_ac[tid]], fifo, + mvmsta->sta_id, tid, buf_size, ssn, wdg_timeout); ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true); if (ret) return -EIO; + /* No need to mark as reserved */ + spin_lock_bh(&mvm->queue_info_lock); + mvm->queue_info[queue].setup_reserved = false; + spin_unlock_bh(&mvm->queue_info_lock); + /* * Even though in theory the peer could have different * aggregation reorder buffer sizes for different sessions, @@ -1056,6 +1066,11 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, mvmsta->agg_tids &= ~BIT(tid); + /* No need to mark as reserved anymore */ + spin_lock_bh(&mvm->queue_info_lock); + mvm->queue_info[txq_id].setup_reserved = false; + spin_unlock_bh(&mvm->queue_info_lock); + switch (tid_data->state) { case IWL_AGG_ON: tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); @@ -1073,14 +1088,15 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, tid_data->ssn = 0xffff; tid_data->state = IWL_AGG_OFF; - mvm->queue_to_mac80211[txq_id] = IWL_INVALID_MAC80211_QUEUE; spin_unlock_bh(&mvmsta->lock); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false); - iwl_mvm_disable_txq(mvm, txq_id, 0); + iwl_mvm_disable_txq(mvm, txq_id, + vif->hw_queue[tid_to_mac80211_ac[tid]], tid, + 0); return 0; case IWL_AGG_STARTING: case IWL_EMPTYING_HW_QUEUE_ADDBA: @@ -1091,7 +1107,6 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, /* No barriers since we are under mutex */ lockdep_assert_held(&mvm->mutex); - mvm->queue_to_mac80211[txq_id] = IWL_INVALID_MAC80211_QUEUE; ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); tid_data->state = IWL_AGG_OFF; @@ -1132,6 +1147,11 @@ int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif, mvmsta->agg_tids &= ~BIT(tid); spin_unlock_bh(&mvmsta->lock); + /* No need to mark as reserved */ + spin_lock_bh(&mvm->queue_info_lock); + mvm->queue_info[txq_id].setup_reserved = false; + spin_unlock_bh(&mvm->queue_info_lock); + if (old_state >= IWL_AGG_ON) { iwl_mvm_drain_sta(mvm, mvmsta, true); if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), true)) @@ -1142,12 +1162,11 @@ int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif, iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false); - iwl_mvm_disable_txq(mvm, tid_data->txq_id, 0); + iwl_mvm_disable_txq(mvm, tid_data->txq_id, + vif->hw_queue[tid_to_mac80211_ac[tid]], tid, + 0); } - mvm->queue_to_mac80211[tid_data->txq_id] = - IWL_INVALID_MAC80211_QUEUE; - return 0; } diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index 6df5aad..ff8b9bd 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -560,15 +560,10 @@ static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm, IWL_DEBUG_TX_QUEUES(mvm, "Can continue DELBA flow ssn = next_recl = %d\n", tid_data->next_reclaimed); - iwl_mvm_disable_txq(mvm, tid_data->txq_id, CMD_ASYNC); + iwl_mvm_disable_txq(mvm, tid_data->txq_id, + vif->hw_queue[tid_to_mac80211_ac[tid]], tid, + CMD_ASYNC); tid_data->state = IWL_AGG_OFF; - /* - * we can't hold the mutex - but since we are after a sequence - * point (call to iwl_mvm_disable_txq(), so we don't even need - * a memory barrier. - */ - mvm->queue_to_mac80211[tid_data->txq_id] = - IWL_INVALID_MAC80211_QUEUE; ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index 06cba97..ad0f169 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright (C) 2015 Intel Deutschland GmbH * * 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 @@ -657,34 +658,143 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) if (mvm->support_umac_log) iwl_mvm_dump_umac_error_log(mvm); } -void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, u16 ssn, - const struct iwl_trans_txq_scd_cfg *cfg, + +int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 minq, u8 maxq) +{ + int i; + + lockdep_assert_held(&mvm->queue_info_lock); + + for (i = minq; i <= maxq; i++) + if (mvm->queue_info[i].hw_queue_refcount == 0 && + !mvm->queue_info[i].setup_reserved) + return i; + + return -ENOSPC; +} + +void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, + u16 ssn, const struct iwl_trans_txq_scd_cfg *cfg, unsigned int wdg_timeout) { - struct iwl_scd_txq_cfg_cmd cmd = { - .scd_queue = queue, - .enable = 1, - .window = cfg->frame_limit, - .sta_id = cfg->sta_id, - .ssn = cpu_to_le16(ssn), - .tx_fifo = cfg->fifo, - .aggregate = cfg->aggregate, - .tid = cfg->tid, - }; + bool enable_queue = true; + + spin_lock_bh(&mvm->queue_info_lock); + + /* Make sure this TID isn't already enabled */ + if (mvm->queue_info[queue].tid_bitmap & BIT(cfg->tid)) { + spin_unlock_bh(&mvm->queue_info_lock); + IWL_ERR(mvm, "Trying to enable TXQ with existing TID %d\n", + cfg->tid); + return; + } - iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn, NULL, wdg_timeout); - WARN(iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, sizeof(cmd), &cmd), - "Failed to configure queue %d on FIFO %d\n", queue, cfg->fifo); + /* Update mappings and refcounts */ + mvm->queue_info[queue].hw_queue_to_mac80211 |= BIT(mac80211_queue); + mvm->queue_info[queue].hw_queue_refcount++; + if (mvm->queue_info[queue].hw_queue_refcount > 1) + enable_queue = false; + mvm->queue_info[queue].tid_bitmap |= BIT(cfg->tid); + + IWL_DEBUG_TX_QUEUES(mvm, + "Enabling TXQ #%d refcount=%d (mac80211 map:0x%x)\n", + queue, mvm->queue_info[queue].hw_queue_refcount, + mvm->queue_info[queue].hw_queue_to_mac80211); + + spin_unlock_bh(&mvm->queue_info_lock); + + /* Send the enabling command if we need to */ + if (enable_queue) { + struct iwl_scd_txq_cfg_cmd cmd = { + .scd_queue = queue, + .enable = 1, + .window = cfg->frame_limit, + .sta_id = cfg->sta_id, + .ssn = cpu_to_le16(ssn), + .tx_fifo = cfg->fifo, + .aggregate = cfg->aggregate, + .tid = cfg->tid, + }; + + iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn, NULL, + wdg_timeout); + WARN(iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, sizeof(cmd), + &cmd), + "Failed to configure queue %d on FIFO %d\n", queue, + cfg->fifo); + } } -void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, u8 flags) +void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, + u8 tid, u8 flags) { struct iwl_scd_txq_cfg_cmd cmd = { .scd_queue = queue, .enable = 0, }; + bool remove_mac_queue = true; int ret; + spin_lock_bh(&mvm->queue_info_lock); + + if (WARN_ON(mvm->queue_info[queue].hw_queue_refcount == 0)) { + spin_unlock_bh(&mvm->queue_info_lock); + return; + } + + mvm->queue_info[queue].tid_bitmap &= ~BIT(tid); + + /* + * If there is another TID with the same AC - don't remove the MAC queue + * from the mapping + */ + if (tid < IWL_MAX_TID_COUNT) { + unsigned long tid_bitmap = + mvm->queue_info[queue].tid_bitmap; + int ac = tid_to_mac80211_ac[tid]; + int i; + + for_each_set_bit(i, &tid_bitmap, IWL_MAX_TID_COUNT) { + if (tid_to_mac80211_ac[i] == ac) + remove_mac_queue = false; + } + } + + if (remove_mac_queue) + mvm->queue_info[queue].hw_queue_to_mac80211 &= + ~BIT(mac80211_queue); + mvm->queue_info[queue].hw_queue_refcount--; + + cmd.enable = mvm->queue_info[queue].hw_queue_refcount ? 1 : 0; + + IWL_DEBUG_TX_QUEUES(mvm, + "Disabling TXQ #%d refcount=%d (mac80211 map:0x%x)\n", + queue, + mvm->queue_info[queue].hw_queue_refcount, + mvm->queue_info[queue].hw_queue_to_mac80211); + + /* If the queue is still enabled - nothing left to do in this func */ + if (cmd.enable) { + spin_unlock_bh(&mvm->queue_info_lock); + return; + } + + /* Make sure queue info is correct even though we overwrite it */ + WARN(mvm->queue_info[queue].hw_queue_refcount || + mvm->queue_info[queue].tid_bitmap || + mvm->queue_info[queue].hw_queue_to_mac80211, + "TXQ #%d info out-of-sync - refcount=%d, mac map=0x%x, tid=0x%x\n", + queue, mvm->queue_info[queue].hw_queue_refcount, + mvm->queue_info[queue].hw_queue_to_mac80211, + mvm->queue_info[queue].tid_bitmap); + + /* If we are here - the queue is freed and we can zero out these vals */ + mvm->queue_info[queue].hw_queue_refcount = 0; + mvm->queue_info[queue].tid_bitmap = 0; + mvm->queue_info[queue].hw_queue_to_mac80211 = 0; + + spin_unlock_bh(&mvm->queue_info_lock); + iwl_trans_txq_disable(mvm->trans, queue, false); ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, flags, sizeof(cmd), &cmd); -- cgit v0.10.2 From 5c1156efebc6b76b4a7daec844c19d2ce1d7bb03 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Wed, 22 Jul 2015 17:59:53 +0300 Subject: iwlwifi: mvm: support enabling a queue with a given ssn When enabling a queue, the default SSN is 0. Allow determining what that SSN should be, if required. This can happen, for example, if a queue gets reconfigured. Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index d2a221d..9d36ba7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -487,17 +487,17 @@ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif) case NL80211_IFTYPE_P2P_DEVICE: iwl_mvm_enable_ac_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE, IWL_MVM_OFFCHANNEL_QUEUE, - IWL_MVM_TX_FIFO_VO, wdg_timeout); + 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, wdg_timeout); + IWL_MVM_TX_FIFO_MCAST, 0, wdg_timeout); /* fall through */ default: for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) iwl_mvm_enable_ac_txq(mvm, vif->hw_queue[ac], vif->hw_queue[ac], - iwl_mvm_ac_to_tx_fifo[ac], + iwl_mvm_ac_to_tx_fifo[ac], 0, wdg_timeout); break; } diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index a7a9619..99182df 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -1366,7 +1366,7 @@ int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 minq, u8 maxq); static inline void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, - u8 fifo, unsigned int wdg_timeout) + u8 fifo, u16 ssn, unsigned int wdg_timeout) { struct iwl_trans_txq_scd_cfg cfg = { .fifo = fifo, @@ -1375,7 +1375,7 @@ void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, .frame_limit = IWL_FRAME_LIMIT, }; - iwl_mvm_enable_txq(mvm, queue, mac80211_queue, 0, &cfg, wdg_timeout); + iwl_mvm_enable_txq(mvm, queue, mac80211_queue, ssn, &cfg, wdg_timeout); } static inline void iwl_mvm_enable_agg_txq(struct iwl_mvm *mvm, int queue, diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 9bda6c9..a9a3eb6 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -235,7 +235,8 @@ static int iwl_mvm_tdls_sta_init(struct iwl_mvm *mvm, for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { iwl_mvm_enable_ac_txq(mvm, mvmsta->hw_queue[ac], mvmsta->hw_queue[ac], - iwl_mvm_ac_to_tx_fifo[ac], wdg_timeout); + iwl_mvm_ac_to_tx_fifo[ac], 0, + wdg_timeout); mvmsta->tfd_queue_msk |= BIT(mvmsta->hw_queue[ac]); } @@ -653,7 +654,7 @@ int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm) /* Map Aux queue to fifo - needs to happen before adding Aux station */ iwl_mvm_enable_ac_txq(mvm, mvm->aux_queue, mvm->aux_queue, - IWL_MVM_TX_FIFO_MCAST, wdg_timeout); + IWL_MVM_TX_FIFO_MCAST, 0, wdg_timeout); /* Allocate aux station and assign to it the aux queue */ ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, BIT(mvm->aux_queue), -- cgit v0.10.2 From ceef91c89480dd18bb3ac51e91280a233d0ca41f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 4 Sep 2015 11:27:25 +0200 Subject: iwlwifi: mvm: stop using DEVICE_POWER_FLAGS_CAM_MSK The firmware has always treated these two bits to mean that powersave is enabled when POWER_SAVE_ENA is set and CAM is clear; it doesn't use them in any non-combined way. Therefore, it's pointless to send it two bits, and the API should be cleaned up. Prepare the driver by removing the CAM bit and using only POWER_SAVE_ENA to indicate whether PS is enabled or not. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h index 7005fa4..c8f3e25 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h @@ -192,16 +192,10 @@ struct iwl_powertable_cmd { /** * enum iwl_device_power_flags - masks for device power command flags * @DEVIC_POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off - * receiver and transmitter. '0' - does not allow. This flag should be - * always set to '1' unless one need to disable actual power down for debug - * purposes. - * @DEVICE_POWER_FLAGS_CAM_MSK: '1' CAM (Continuous Active Mode) is set, meaning - * that power management is disabled. '0' Power management is enabled, one - * of power schemes is applied. + * receiver and transmitter. '0' - does not allow. */ enum iwl_device_power_flags { DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0), - DEVICE_POWER_FLAGS_CAM_MSK = BIT(13), }; /** diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c index 21a858d..723b537 100644 --- a/drivers/net/wireless/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/iwlwifi/mvm/power.c @@ -471,14 +471,14 @@ static int iwl_mvm_power_send_cmd(struct iwl_mvm *mvm, int iwl_mvm_power_update_device(struct iwl_mvm *mvm) { struct iwl_device_power_cmd cmd = { - .flags = cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK), + .flags = 0, }; if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) mvm->ps_disabled = true; - if (mvm->ps_disabled) - cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_CAM_MSK); + if (!mvm->ps_disabled) + cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK); #ifdef CONFIG_IWLWIFI_DEBUGFS if ((mvm->cur_ucode == IWL_UCODE_WOWLAN) ? mvm->disable_power_off_d3 : -- cgit v0.10.2 From cc60c6e92907d7d1380353b46d7db9826a3622f2 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Sat, 19 Sep 2015 23:37:45 +0300 Subject: iwlwifi: mvm: rs: improve rate debug messages Pretty print the rate full details to ease debugging. Signed-off-by: Eyal Shapira Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 5ae9c8a..98f4d78 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -524,14 +524,56 @@ static const char *rs_pretty_lq_type(enum iwl_table_type type) return lq_types[type]; } +static char *rs_pretty_rate(const struct rs_rate *rate) +{ + static char buf[40]; + static const char * const legacy_rates[] = { + [IWL_RATE_1M_INDEX] = "1M", + [IWL_RATE_2M_INDEX] = "2M", + [IWL_RATE_5M_INDEX] = "5.5M", + [IWL_RATE_11M_INDEX] = "11M", + [IWL_RATE_6M_INDEX] = "6M", + [IWL_RATE_9M_INDEX] = "9M", + [IWL_RATE_12M_INDEX] = "12M", + [IWL_RATE_18M_INDEX] = "18M", + [IWL_RATE_24M_INDEX] = "24M", + [IWL_RATE_36M_INDEX] = "36M", + [IWL_RATE_48M_INDEX] = "48M", + [IWL_RATE_54M_INDEX] = "54M", + }; + static const char *const ht_vht_rates[] = { + [IWL_RATE_MCS_0_INDEX] = "MCS0", + [IWL_RATE_MCS_1_INDEX] = "MCS1", + [IWL_RATE_MCS_2_INDEX] = "MCS2", + [IWL_RATE_MCS_3_INDEX] = "MCS3", + [IWL_RATE_MCS_4_INDEX] = "MCS4", + [IWL_RATE_MCS_5_INDEX] = "MCS5", + [IWL_RATE_MCS_6_INDEX] = "MCS6", + [IWL_RATE_MCS_7_INDEX] = "MCS7", + [IWL_RATE_MCS_8_INDEX] = "MCS8", + [IWL_RATE_MCS_9_INDEX] = "MCS9", + }; + const char *rate_str; + + if (is_type_legacy(rate->type)) + rate_str = legacy_rates[rate->index]; + else if (is_type_ht(rate->type) || is_type_vht(rate->type)) + rate_str = ht_vht_rates[rate->index]; + else + rate_str = "BAD_RATE"; + + sprintf(buf, "(%s|%s|%s)", rs_pretty_lq_type(rate->type), + rs_pretty_ant(rate->ant), rate_str); + return buf; +} + static inline void rs_dump_rate(struct iwl_mvm *mvm, const struct rs_rate *rate, const char *prefix) { IWL_DEBUG_RATE(mvm, - "%s: (%s: %d) ANT: %s BW: %d SGI: %d LDPC: %d STBC: %d\n", - prefix, rs_pretty_lq_type(rate->type), - rate->index, rs_pretty_ant(rate->ant), - rate->bw, rate->sgi, rate->ldpc, rate->stbc); + "%s: %s BW: %d SGI: %d LDPC: %d STBC: %d\n", + prefix, rs_pretty_rate(rate), rate->bw, + rate->sgi, rate->ldpc, rate->stbc); } static void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window) @@ -2174,9 +2216,9 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, if ((fail_count < IWL_MVM_RS_RATE_MIN_FAILURE_TH) && (window->success_counter < IWL_MVM_RS_RATE_MIN_SUCCESS_TH)) { IWL_DEBUG_RATE(mvm, - "(%s: %d): Test Window: succ %d total %d\n", - rs_pretty_lq_type(rate->type), - index, window->success_counter, window->counter); + "%s: Test Window: succ %d total %d\n", + rs_pretty_rate(rate), + window->success_counter, window->counter); /* Can't calculate this yet; not enough history */ window->average_tpt = IWL_INVALID_VALUE; @@ -2253,8 +2295,8 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, high_tpt = tbl->win[high].average_tpt; IWL_DEBUG_RATE(mvm, - "(%s: %d): cur_tpt %d SR %d low %d high %d low_tpt %d high_tpt %d\n", - rs_pretty_lq_type(rate->type), index, current_tpt, sr, + "%s: cur_tpt %d SR %d low %d high %d low_tpt %d high_tpt %d\n", + rs_pretty_rate(rate), current_tpt, sr, low, high, low_tpt, high_tpt); scale_action = rs_get_rate_action(mvm, tbl, sr, low, high, -- cgit v0.10.2 From dfa1325a0865da0509f045ddc4c99b8653517f6b Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Sat, 19 Sep 2015 23:46:32 +0300 Subject: iwlwifi: mvm: rs: remove overflowing debug message This message isn't very useful and creates clutter. Remove it. Signed-off-by: Eyal Shapira Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 98f4d78..49cf311 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -2584,7 +2584,6 @@ static struct rs_rate *rs_get_optimal_rate(struct iwl_mvm *mvm, } } - rs_dump_rate(mvm, rate, "OPTIMAL RATE"); return rate; } -- cgit v0.10.2 From 40ce5ed1e9d5c80819733c4fdc40a2b6c531d981 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Sat, 19 Sep 2015 23:54:38 +0300 Subject: iwlwifi: mvm: rs: minor indentation fix Indentation was off a bit. Fix it. Signed-off-by: Eyal Shapira Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 49cf311..2bf2a08 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -604,8 +604,8 @@ static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type) } static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_data, u8 tid, - struct ieee80211_sta *sta) + struct iwl_lq_sta *lq_data, u8 tid, + struct ieee80211_sta *sta) { int ret = -EAGAIN; -- cgit v0.10.2 From ed21a384bbd2b011289dd1b5cab7eebc7ebeda11 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Mon, 21 Sep 2015 20:03:22 +0300 Subject: iwlwifi: mvm: rs: fix success ratio comparison in rs_get_best_rate success_ratio is actually 128 * SR in percentage while IWL_MVM_RS_SR_NO_DECREASE is 85%. Fix this by using RS_PERCENT(). This bug caused the if branch to be always executed. This in turn led to always selecting a rate, following a column switch, in which the expected throughput would exceed the best expected current throughput. In some scenarios where the success ratio isn't >85% such a rate could be too aggressive leading us to avoid the new column. This has the potential of causing sub optimal performance. Reported-by: Moshe Harel Signed-off-by: Eyal Shapira Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 2bf2a08..435e1e4 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -1527,7 +1527,7 @@ static s32 rs_get_best_rate(struct iwl_mvm *mvm, u32 target_tpt; int rate_idx; - if (success_ratio > IWL_MVM_RS_SR_NO_DECREASE) { + if (success_ratio >= RS_PERCENT(IWL_MVM_RS_SR_NO_DECREASE)) { target_tpt = 100 * expected_current_tpt; IWL_DEBUG_RATE(mvm, "SR %d high. Find rate exceeding EXPECTED_CURRENT %d\n", @@ -1535,7 +1535,7 @@ static s32 rs_get_best_rate(struct iwl_mvm *mvm, } else { target_tpt = lq_sta->last_tpt; IWL_DEBUG_RATE(mvm, - "SR %d not thag good. Find rate exceeding ACTUAL_TPT %d\n", + "SR %d not that good. Find rate exceeding ACTUAL_TPT %d\n", success_ratio, target_tpt); } -- cgit v0.10.2 From 5736b7eba362ee304335808325ad60e2249ac670 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 22 Sep 2015 10:51:17 +0200 Subject: iwlwifi: remove IWL3165_UCODE_API_OK and _MIN As the 3165 device uses the same firmware as 7265-D and currently all 7000 series (including 3160/3165) use the same API versions remove IWL3165_UCODE_API_OK and _MIN. We might have to put them back if firmware support ever splits, but in that case might also have to add a different MODULE_FIRMWARE statement. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c index 5b25f36..d561181 100644 --- a/drivers/net/wireless/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/iwlwifi/iwl-7000.c @@ -73,11 +73,9 @@ /* Oldest version we won't warn about */ #define IWL7260_UCODE_API_OK 13 -#define IWL3165_UCODE_API_OK 13 /* Lowest firmware API version supported */ #define IWL7260_UCODE_API_MIN 13 -#define IWL3165_UCODE_API_MIN 13 /* NVM versions */ #define IWL7260_NVM_VERSION 0x0a1d @@ -269,11 +267,6 @@ const struct iwl_cfg iwl3165_2ac_cfg = { .name = "Intel(R) Dual Band Wireless AC 3165", .fw_name_pre = IWL7265D_FW_PRE, IWL_DEVICE_7000, - /* sparse doens't like the re-assignment but it is safe */ -#ifndef __CHECKER__ - .ucode_api_ok = IWL3165_UCODE_API_OK, - .ucode_api_min = IWL3165_UCODE_API_MIN, -#endif .ht_params = &iwl7000_ht_params, .nvm_ver = IWL3165_NVM_VERSION, .nvm_calib_ver = IWL3165_TX_POWER_VERSION, -- cgit v0.10.2 From 7c0ebd7870ce212d370a97d3d8ff5129edcc2323 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 22 Sep 2015 12:28:42 +0200 Subject: iwlwifi: mvm: minor rx code cleanup Clean up variable initialisation slightly. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c index 0a6d47c..5b58f53 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/iwlwifi/mvm/rx.c @@ -346,8 +346,8 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, /* This is fine since we don't support multiple AP interfaces */ sta = ieee80211_find_sta_by_ifaddr(mvm->hw, hdr->addr2, NULL); if (sta) { - struct iwl_mvm_sta *mvmsta; - mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status); if (iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_RSSI) && -- cgit v0.10.2 From 1191f646bb913feba4239a7ca45e5edb09324869 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Sat, 19 Sep 2015 23:48:45 +0300 Subject: iwlwifi: mvm: rs: dynamically switch between 80MHz and 20MHz in some scenarios This is a tweak which has been shown to improve performance when moving away from the AP while working in 80Mhz. When RS decides to go down to 80MHz SISO MCS0 instead switch to 20MHz MCS4. Go back to 80MHz MCS1 if RS can sustain 20MHz MCS5. Signed-off-by: Eyal Shapira Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/constants.h b/drivers/net/wireless/iwlwifi/mvm/constants.h index b8ee312..a3ca6db 100644 --- a/drivers/net/wireless/iwlwifi/mvm/constants.h +++ b/drivers/net/wireless/iwlwifi/mvm/constants.h @@ -102,6 +102,7 @@ #define IWL_MVM_QUOTA_THRESHOLD 4 #define IWL_MVM_RS_RSSI_BASED_INIT_RATE 0 #define IWL_MVM_RS_DISABLE_P2P_MIMO 0 +#define IWL_MVM_RS_80_20_FAR_RANGE_TWEAK 1 #define IWL_MVM_TOF_IS_RESPONDER 0 #define IWL_MVM_RS_NUM_TRY_BEFORE_ANT_TOGGLE 1 #define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE 2 diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 435e1e4..34d98b2 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -1664,6 +1664,51 @@ static void rs_update_rate_tbl(struct iwl_mvm *mvm, iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false); } +static bool rs_tweak_rate_tbl(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl, + enum rs_action scale_action) +{ + if (sta->bandwidth != IEEE80211_STA_RX_BW_80) + return false; + + if (!is_vht_siso(&tbl->rate)) + return false; + + if ((tbl->rate.bw == RATE_MCS_CHAN_WIDTH_80) && + (tbl->rate.index == IWL_RATE_MCS_0_INDEX) && + (scale_action == RS_ACTION_DOWNSCALE)) { + tbl->rate.bw = RATE_MCS_CHAN_WIDTH_20; + tbl->rate.index = IWL_RATE_MCS_4_INDEX; + IWL_DEBUG_RATE(mvm, "Switch 80Mhz SISO MCS0 -> 20Mhz MCS4\n"); + goto tweaked; + } + + /* Go back to 80Mhz MCS1 only if we've established that 20Mhz MCS5 is + * sustainable, i.e. we're past the test window. We can't go back + * if MCS5 is just tested as this will happen always after switching + * to 20Mhz MCS4 because the rate stats are cleared. + */ + if ((tbl->rate.bw == RATE_MCS_CHAN_WIDTH_20) && + (((tbl->rate.index == IWL_RATE_MCS_5_INDEX) && + (scale_action == RS_ACTION_STAY)) || + ((tbl->rate.index > IWL_RATE_MCS_5_INDEX) && + (scale_action == RS_ACTION_UPSCALE)))) { + tbl->rate.bw = RATE_MCS_CHAN_WIDTH_80; + tbl->rate.index = IWL_RATE_MCS_1_INDEX; + IWL_DEBUG_RATE(mvm, "Switch 20Mhz SISO MCS5 -> 80Mhz MCS1\n"); + goto tweaked; + } + + return false; + +tweaked: + rs_set_expected_tpt_table(lq_sta, tbl); + rs_rate_scale_clear_tbl_windows(mvm, tbl); + return true; +} + static enum rs_column rs_get_next_column(struct iwl_mvm *mvm, struct iwl_lq_sta *lq_sta, struct ieee80211_sta *sta, @@ -2347,6 +2392,8 @@ lq_update: /* Replace uCode's rate table for the destination station. */ if (update_lq) { tbl->rate.index = index; + if (IWL_MVM_RS_80_20_FAR_RANGE_TWEAK) + rs_tweak_rate_tbl(mvm, sta, lq_sta, tbl, scale_action); rs_update_rate_tbl(mvm, sta, lq_sta, tbl); } -- cgit v0.10.2 From e6c21be665cab08325ee1f99e76497b5ffa924a9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 24 Sep 2015 22:15:02 +0200 Subject: iwlwifi: mvm: fix signedness warnings in ToF debugfs Using an int* instead of u32* as the kstrtou32() output argument obviously results in signedness warnings, change that. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c index 5dc1686..398bef6 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c @@ -511,7 +511,8 @@ static ssize_t iwl_dbgfs_tof_enable_write(struct ieee80211_vif *vif, { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm *mvm = mvmvif->mvm; - int value, ret = -EINVAL; + u32 value; + int ret = -EINVAL; char *data; mutex_lock(&mvm->mutex); @@ -599,7 +600,8 @@ static ssize_t iwl_dbgfs_tof_responder_params_write(struct ieee80211_vif *vif, { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm *mvm = mvmvif->mvm; - int value, ret = 0; + u32 value; + int ret = 0; char *data; mutex_lock(&mvm->mutex); @@ -822,7 +824,8 @@ static ssize_t iwl_dbgfs_tof_range_request_write(struct ieee80211_vif *vif, { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm *mvm = mvmvif->mvm; - int value, ret = 0; + u32 value; + int ret = 0; char *data; mutex_lock(&mvm->mutex); @@ -1023,7 +1026,8 @@ static ssize_t iwl_dbgfs_tof_range_req_ext_write(struct ieee80211_vif *vif, { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm *mvm = mvmvif->mvm; - int value, ret = 0; + u32 value; + int ret = 0; char *data; mutex_lock(&mvm->mutex); @@ -1127,8 +1131,8 @@ static ssize_t iwl_dbgfs_tof_range_abort_write(struct ieee80211_vif *vif, { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm *mvm = mvmvif->mvm; - int value, ret = 0; - int abort_id; + u32 value; + int abort_id, ret = 0; char *data; mutex_lock(&mvm->mutex); -- cgit v0.10.2 From 91fac940896b345d10e503ed7e1f54f85b3b9318 Mon Sep 17 00:00:00 2001 From: Moshe Harel Date: Wed, 2 Sep 2015 12:45:12 +0300 Subject: iwlwifi: nvm: add nvm phy_sku section to debugfs The only NVM section not captured in debugfs. Signed-off-by: Moshe Harel Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index 3b8481f..9b4fbb8 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -1495,6 +1495,9 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) if (!debugfs_create_blob("nvm_prod", S_IRUSR, mvm->debugfs_dir, &mvm->nvm_prod_blob)) goto err; + if (!debugfs_create_blob("nvm_phy_sku", S_IRUSR, + mvm->debugfs_dir, &mvm->nvm_phy_sku_blob)) + goto err; /* * Create a symlink with mac80211. It will be removed when mac80211 diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 99182df..f4e22f5 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -688,6 +688,7 @@ struct iwl_mvm { struct debugfs_blob_wrapper nvm_sw_blob; struct debugfs_blob_wrapper nvm_calib_blob; struct debugfs_blob_wrapper nvm_prod_blob; + struct debugfs_blob_wrapper nvm_phy_sku_blob; struct iwl_mvm_frame_stats drv_rx_stats; spinlock_t drv_stats_lock; diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index 0b3ffd5..4e4a680 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -564,6 +564,10 @@ int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic) mvm->nvm_prod_blob.data = temp; mvm->nvm_prod_blob.size = ret; break; + case NVM_SECTION_TYPE_PHY_SKU: + mvm->nvm_phy_sku_blob.data = temp; + mvm->nvm_phy_sku_blob.size = ret; + break; default: if (section == mvm->cfg->nvm_hw_section_num) { mvm->nvm_hw_blob.data = temp; -- cgit v0.10.2 From fe96cc73c9f405a0bdc90504048235dfd0384582 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 24 Sep 2015 22:19:43 +0200 Subject: iwlwifi: mvm: make threshold temperatures unsigned There's no need to have negative threshold temperatures, so make them unsigned to avoid signedness warnings in debugfs. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h index 939fa22..9109708 100644 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/iwlwifi/iwl-config.h @@ -223,13 +223,13 @@ struct iwl_tt_tx_backoff { * @support_tx_backoff: Support tx-backoff? */ struct iwl_tt_params { - s32 ct_kill_entry; - s32 ct_kill_exit; + u32 ct_kill_entry; + u32 ct_kill_exit; u32 ct_kill_duration; - s32 dynamic_smps_entry; - s32 dynamic_smps_exit; - s32 tx_protection_entry; - s32 tx_protection_exit; + u32 dynamic_smps_entry; + u32 dynamic_smps_exit; + u32 tx_protection_entry; + u32 tx_protection_exit; struct iwl_tt_tx_backoff tx_backoff[TT_TX_BACKOFF_SIZE]; bool support_ct_kill; bool support_dynamic_smps; -- cgit v0.10.2 From d3f555f493b037eb688adda6d8a682e9b69211ed Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 16 Sep 2015 12:21:32 +0200 Subject: iwlwifi: size firmware flags memory correctly Instead of relying on a hard-coded constant of a maximum of 64 API and capability bits, add a new enum value after the others that will then always track the number of used bits in the API/capabilities. We thus no longer need to maintain the maximum number, and on 32-bit platforms even (currently) reduce the number of bits kept in memory. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index a86aa5b..463cadf 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -450,7 +450,7 @@ static int iwl_set_ucode_api_flags(struct iwl_drv *drv, const u8 *data, u32 api_flags = le32_to_cpu(ucode_api->api_flags); int i; - if (api_index >= IWL_API_MAX_BITS / 32) { + if (api_index >= DIV_ROUND_UP(NUM_IWL_UCODE_TLV_API, 32)) { IWL_ERR(drv, "api_index larger than supported by driver\n"); /* don't return an error so we can load FW that has more bits */ return 0; @@ -472,7 +472,7 @@ static int iwl_set_ucode_capabilities(struct iwl_drv *drv, const u8 *data, u32 api_flags = le32_to_cpu(ucode_capa->api_capa); int i; - if (api_index >= IWL_CAPABILITIES_MAX_BITS / 32) { + if (api_index >= DIV_ROUND_UP(NUM_IWL_UCODE_TLV_CAPA, 32)) { IWL_ERR(drv, "api_index larger than supported by driver\n"); /* don't return an error so we can load FW that has more bits */ return 0; diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h index 352d245..72ddd4a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h @@ -254,6 +254,8 @@ typedef unsigned int __bitwise__ iwl_ucode_tlv_api_t; * instead of 3. * @IWL_UCODE_TLV_API_TX_POWER_CHAIN: TX power API has larger command size * (command version 3) that supports per-chain limits + * + * @NUM_IWL_UCODE_TLV_API: number of bits used */ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_BT_COEX_SPLIT = (__force iwl_ucode_tlv_api_t)3, @@ -264,6 +266,12 @@ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_NEW_VERSION = (__force iwl_ucode_tlv_api_t)20, IWL_UCODE_TLV_API_EXT_SCAN_PRIORITY = (__force iwl_ucode_tlv_api_t)24, IWL_UCODE_TLV_API_TX_POWER_CHAIN = (__force iwl_ucode_tlv_api_t)27, + + NUM_IWL_UCODE_TLV_API +#ifdef __CHECKER__ + /* sparse says it cannot increment the previous enum member */ + = 128 +#endif }; typedef unsigned int __bitwise__ iwl_ucode_tlv_capa_t; @@ -298,6 +306,8 @@ 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 + * + * @NUM_IWL_UCODE_TLV_CAPA: number of bits used */ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_D0I3_SUPPORT = (__force iwl_ucode_tlv_capa_t)0, @@ -320,6 +330,12 @@ 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, + + NUM_IWL_UCODE_TLV_CAPA +#ifdef __CHECKER__ + /* sparse says it cannot increment the previous enum member */ + = 128 +#endif }; /* The default calibrate table size if not specified by firmware file */ @@ -330,9 +346,6 @@ enum iwl_ucode_tlv_capa { /* The default max probe length if not specified by the firmware file */ #define IWL_DEFAULT_MAX_PROBE_LENGTH 200 -#define IWL_API_MAX_BITS 64 -#define IWL_CAPABILITIES_MAX_BITS 64 - /* * For 16.0 uCode and above, there is no differentiation between sections, * just an offset to the HW address. diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index 45e7321..84ec0ce 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -105,8 +105,8 @@ struct iwl_ucode_capabilities { u32 n_scan_channels; u32 standard_phy_calibration_size; u32 flags; - unsigned long _api[BITS_TO_LONGS(IWL_API_MAX_BITS)]; - unsigned long _capa[BITS_TO_LONGS(IWL_CAPABILITIES_MAX_BITS)]; + unsigned long _api[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_API)]; + unsigned long _capa[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_CAPA)]; }; static inline bool -- cgit v0.10.2 From 0316d30ea3e66379cd30ed70a114bc282159bb4c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 22 May 2015 13:41:07 +0200 Subject: iwlwifi: mvm: add minimal multi-RXQ infrastructure Since the new multi-queue capability depends on a new firmware API, we can already add some code for it. If the new API is present, a new opmode ops struct is used that handles the new rx_rss method. For now, only restructure the RX handling to distinguish between the two. Future patches will convert the new infrastructure to actually use the new RX descriptor layout. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index f4e22f5..0d3aff1 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -970,6 +970,12 @@ static inline bool iwl_mvm_is_csum_supported(struct iwl_mvm *mvm) IWL_UCODE_TLV_CAPA_CSUM_SUPPORT); } +static inline bool iwl_mvm_has_new_rx_api(struct iwl_mvm *mvm) +{ + /* firmware flag isn't defined yet */ + return false; +} + extern const u8 iwl_mvm_ac_to_tx_fifo[]; struct iwl_rate_info { diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 0e8166f..064c100 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -89,6 +89,7 @@ MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); MODULE_LICENSE("GPL"); static const struct iwl_op_mode_ops iwl_mvm_ops; +static const struct iwl_op_mode_ops iwl_mvm_ops_mq; struct iwl_mvm_mod_params iwlmvm_mod_params = { .power_scheme = IWL_POWER_SCHEME_BPS, @@ -424,7 +425,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, hw->max_tx_aggregation_subframes = cfg->max_tx_agg_size; op_mode = hw->priv; - op_mode->ops = &iwl_mvm_ops; mvm = IWL_OP_MODE_GET_MVM(op_mode); mvm->dev = trans->dev; @@ -433,6 +433,15 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, mvm->fw = fw; mvm->hw = hw; + if (iwl_mvm_has_new_rx_api(mvm)) { + op_mode->ops = &iwl_mvm_ops_mq; + } else { + op_mode->ops = &iwl_mvm_ops; + + if (WARN_ON(trans->num_rx_queues > 1)) + goto out_free; + } + mvm->restart_fw = iwlwifi_mod_params.restart_fw ? -1 : 0; mvm->aux_queue = 15; @@ -719,21 +728,11 @@ static inline void iwl_mvm_rx_check_trigger(struct iwl_mvm *mvm, } } -static void iwl_mvm_rx_dispatch(struct iwl_op_mode *op_mode, - struct napi_struct *napi, - struct iwl_rx_cmd_buffer *rxb) +static void iwl_mvm_rx_common(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_rx_packet *pkt) { - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - u8 i; - - if (likely(pkt->hdr.cmd == REPLY_RX_MPDU_CMD)) { - iwl_mvm_rx_rx_mpdu(mvm, napi, rxb); - return; - } else if (pkt->hdr.cmd == REPLY_RX_PHY_CMD) { - iwl_mvm_rx_rx_phy_cmd(mvm, rxb); - return; - } + int i; iwl_mvm_rx_check_trigger(mvm, pkt); @@ -773,6 +772,36 @@ static void iwl_mvm_rx_dispatch(struct iwl_op_mode *op_mode, } } +static void iwl_mvm_rx(struct iwl_op_mode *op_mode, + struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + + if (likely(pkt->hdr.cmd == REPLY_RX_MPDU_CMD)) + iwl_mvm_rx_rx_mpdu(mvm, napi, rxb); + else if (pkt->hdr.cmd == REPLY_RX_PHY_CMD) + iwl_mvm_rx_rx_phy_cmd(mvm, rxb); + else + iwl_mvm_rx_common(mvm, rxb, pkt); +} + +static void iwl_mvm_rx_mq(struct iwl_op_mode *op_mode, + struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + + if (likely(pkt->hdr.cmd == REPLY_RX_MPDU_CMD)) + iwl_mvm_rx_rx_mpdu(mvm, napi, rxb); + else if (pkt->hdr.cmd == REPLY_RX_PHY_CMD) + iwl_mvm_rx_rx_phy_cmd(mvm, rxb); + else + iwl_mvm_rx_common(mvm, rxb, pkt); +} + static void iwl_mvm_stop_sw_queue(struct iwl_op_mode *op_mode, int queue) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); @@ -1366,17 +1395,38 @@ int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode) return _iwl_mvm_exit_d0i3(mvm); } +#define IWL_MVM_COMMON_OPS \ + /* these could be differentiated */ \ + .queue_full = iwl_mvm_stop_sw_queue, \ + .queue_not_full = iwl_mvm_wake_sw_queue, \ + .hw_rf_kill = iwl_mvm_set_hw_rfkill_state, \ + .free_skb = iwl_mvm_free_skb, \ + .nic_error = iwl_mvm_nic_error, \ + .cmd_queue_full = iwl_mvm_cmd_queue_full, \ + .nic_config = iwl_mvm_nic_config, \ + .enter_d0i3 = iwl_mvm_enter_d0i3, \ + .exit_d0i3 = iwl_mvm_exit_d0i3, \ + /* as we only register one, these MUST be common! */ \ + .start = iwl_op_mode_mvm_start, \ + .stop = iwl_op_mode_mvm_stop + static const struct iwl_op_mode_ops iwl_mvm_ops = { - .start = iwl_op_mode_mvm_start, - .stop = iwl_op_mode_mvm_stop, - .rx = iwl_mvm_rx_dispatch, - .queue_full = iwl_mvm_stop_sw_queue, - .queue_not_full = iwl_mvm_wake_sw_queue, - .hw_rf_kill = iwl_mvm_set_hw_rfkill_state, - .free_skb = iwl_mvm_free_skb, - .nic_error = iwl_mvm_nic_error, - .cmd_queue_full = iwl_mvm_cmd_queue_full, - .nic_config = iwl_mvm_nic_config, - .enter_d0i3 = iwl_mvm_enter_d0i3, - .exit_d0i3 = iwl_mvm_exit_d0i3, + IWL_MVM_COMMON_OPS, + .rx = iwl_mvm_rx, +}; + +static void iwl_mvm_rx_mq_rss(struct iwl_op_mode *op_mode, + struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb, + unsigned int queue) +{ + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + + iwl_mvm_rx_rx_mpdu(mvm, napi, rxb); +} + +static const struct iwl_op_mode_ops iwl_mvm_ops_mq = { + IWL_MVM_COMMON_OPS, + .rx = iwl_mvm_rx_mq, + .rx_rss = iwl_mvm_rx_mq_rss, }; -- cgit v0.10.2 From 0a15afd2eaceceff5be4c8b7166f01c1a68e9642 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Thu, 1 Oct 2015 15:21:08 -0700 Subject: vrf: fix a kernel warning This fixes: tried to remove device ip6gre0 from (null) ------------[ cut here ]------------ kernel BUG at net/core/dev.c:5219! invalid opcode: 0000 [#1] SMP DEBUG_PAGEALLOC CPU: 3 PID: 161 Comm: kworker/u8:2 Not tainted 4.3.0-rc2+ #1142 Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 Workqueue: netns cleanup_net task: ffff8800d784a9c0 ti: ffff8800d74a4000 task.ti: ffff8800d74a4000 RIP: 0010:[] [] __netdev_adjacent_dev_remove+0x40/0xec RSP: 0018:ffff8800d74a7a98 EFLAGS: 00010282 RAX: 000000000000002a RBX: 0000000000000000 RCX: 0000000000000000 RDX: ffff88011adcf701 RSI: ffff88011adccbf8 RDI: ffff88011adccbf8 RBP: ffff8800d74a7ab8 R08: 0000000000000001 R09: 0000000000000000 R10: ffffffff81d190ff R11: 00000000ffffffff R12: ffff8800d599e7c0 R13: 0000000000000000 R14: ffff8800d599e890 R15: ffffffff82385e00 FS: 0000000000000000(0000) GS:ffff88011ac00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 00007ffd6f003000 CR3: 000000000220c000 CR4: 00000000000006e0 Stack: 0000000000000000 ffff8800d599e7c0 0000000000000b00 ffff8800d599e8a0 ffff8800d74a7ad8 ffffffff817f0861 0000000000000000 ffff8800d599e7c0 ffff8800d74a7af8 ffffffff817f088f 0000000000000000 ffff8800d599e7c0 Call Trace: [] __netdev_adjacent_dev_unlink+0x1e/0x35 [] __netdev_adjacent_dev_unlink_neighbour+0x17/0x41 [] netdev_upper_dev_unlink+0x6c/0x13d [] vrf_del_slave+0x26/0x7d [] vrf_device_event+0x2f/0x34 [] notifier_call_chain+0x75/0x9c [] raw_notifier_call_chain+0x14/0x16 [] call_netdevice_notifiers_info+0x52/0x59 [] call_netdevice_notifiers+0x13/0x15 [] rollback_registered_many+0x14f/0x24f [] unregister_netdevice_many+0x19/0x64 [] ip6gre_exit_net+0x163/0x177 [] ops_exit_list+0x44/0x55 [] cleanup_net+0x193/0x226 [] process_one_work+0x26c/0x4d8 [] ? process_one_work+0x170/0x4d8 [] worker_thread+0x1df/0x2c2 [] ? process_scheduled_works+0x2f/0x2f [] ? process_scheduled_works+0x2f/0x2f [] kthread+0xd4/0xdc [] ? trace_hardirqs_on_caller+0x17d/0x199 [] ? __kthread_parkme+0x83/0x83 [] ret_from_fork+0x3f/0x70 [] ? __kthread_parkme+0x83/0x83 Fixes: 93a7e7e837af ("net: Remove the now unused vrf_ptr") Cc: David Ahern Signed-off-by: Cong Wang Acked-by: David Ahern Signed-off-by: David S. Miller diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 64f2ab6..4743963 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -672,7 +672,7 @@ static int vrf_device_event(struct notifier_block *unused, if (event == NETDEV_UNREGISTER) { struct net_device *vrf_dev; - if (netif_is_l3_master(dev)) + if (!vrf_is_slave(dev) || netif_is_l3_master(dev)) goto out; vrf_dev = netdev_master_upper_dev_get(dev); -- cgit v0.10.2 From bab18991871545dfbd10c931eb0fe8f7637156a9 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 2 Oct 2015 15:17:33 +0200 Subject: bpf, seccomp: prepare for upcoming criu support The current ongoing effort to dump existing cBPF seccomp filters back to user space requires to hold the pre-transformed instructions like we do in case of socket filters from sk_attach_filter() side, so they can be reloaded in original form at a later point in time by utilities such as criu. To prepare for this, simply extend the bpf_prog_create_from_user() API to hold a flag that tells whether we should store the original or not. Also, fanout filters could make use of that in future for things like diag. While fanout filters already use bpf_prog_destroy(), move seccomp over to them as well to handle original programs when present. Signed-off-by: Daniel Borkmann Cc: Tycho Andersen Cc: Pavel Emelyanov Cc: Kees Cook Cc: Andy Lutomirski Cc: Alexei Starovoitov Tested-by: Tycho Andersen Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller diff --git a/include/linux/filter.h b/include/linux/filter.h index 3d5fd24..1bbce14 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -411,7 +411,7 @@ typedef int (*bpf_aux_classic_check_t)(struct sock_filter *filter, int bpf_prog_create(struct bpf_prog **pfp, struct sock_fprog_kern *fprog); int bpf_prog_create_from_user(struct bpf_prog **pfp, struct sock_fprog *fprog, - bpf_aux_classic_check_t trans); + bpf_aux_classic_check_t trans, bool save_orig); void bpf_prog_destroy(struct bpf_prog *fp); int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk); diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 5bd4779..06858a7 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -370,7 +370,7 @@ static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog) return ERR_PTR(-ENOMEM); ret = bpf_prog_create_from_user(&sfilter->prog, fprog, - seccomp_check_filter); + seccomp_check_filter, false); if (ret < 0) { kfree(sfilter); return ERR_PTR(ret); @@ -469,7 +469,7 @@ void get_seccomp_filter(struct task_struct *tsk) static inline void seccomp_filter_free(struct seccomp_filter *filter) { if (filter) { - bpf_prog_free(filter->prog); + bpf_prog_destroy(filter->prog); kfree(filter); } } diff --git a/net/core/filter.c b/net/core/filter.c index 53a5036..da3e535 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1084,16 +1084,18 @@ EXPORT_SYMBOL_GPL(bpf_prog_create); * @pfp: the unattached filter that is created * @fprog: the filter program * @trans: post-classic verifier transformation handler + * @save_orig: save classic BPF program * * This function effectively does the same as bpf_prog_create(), only * that it builds up its insns buffer from user space provided buffer. * It also allows for passing a bpf_aux_classic_check_t handler. */ int bpf_prog_create_from_user(struct bpf_prog **pfp, struct sock_fprog *fprog, - bpf_aux_classic_check_t trans) + bpf_aux_classic_check_t trans, bool save_orig) { unsigned int fsize = bpf_classic_proglen(fprog); struct bpf_prog *fp; + int err; /* Make sure new filter is there and in the right amounts. */ if (fprog->filter == NULL) @@ -1109,12 +1111,16 @@ int bpf_prog_create_from_user(struct bpf_prog **pfp, struct sock_fprog *fprog, } fp->len = fprog->len; - /* Since unattached filters are not copied back to user - * space through sk_get_filter(), we do not need to hold - * a copy here, and can spare us the work. - */ fp->orig_prog = NULL; + if (save_orig) { + err = bpf_prog_store_orig_filter(fp, fprog); + if (err) { + __bpf_prog_free(fp); + return -ENOMEM; + } + } + /* bpf_prepare_filter() already takes care of freeing * memory in case something goes wrong. */ diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index aa4b15c..81c900f 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1567,7 +1567,7 @@ static int fanout_set_data_cbpf(struct packet_sock *po, char __user *data, if (copy_from_user(&fprog, data, len)) return -EFAULT; - ret = bpf_prog_create_from_user(&new, &fprog, NULL); + ret = bpf_prog_create_from_user(&new, &fprog, NULL, false); if (ret) return ret; -- cgit v0.10.2 From 7b0378f517fa1a32b5c8384248d2f8bf79c7c2ae Mon Sep 17 00:00:00 2001 From: Dean Jenkins Date: Fri, 2 Oct 2015 14:29:04 +0100 Subject: asix: Rename remaining and size for clarity The Data header synchronisation is easier to understand if the variables "remaining" and "size" are renamed. Therefore, the lifetime of the "remaining" variable exists outside of asix_rx_fixup_internal() and is used to indicate any remaining pending bytes of the Ethernet frame that need to be obtained from the next socket buffer. This allows an Ethernet frame to span across multiple socket buffers. "size" is now local to asix_rx_fixup_internal() and contains the size read from the Data header 32-bit word. Add "copy_length" to hold the number of the Ethernet frame bytes (maybe a part of a full frame) that are to be copied out of the socket buffer. Signed-off-by: Dean Jenkins Signed-off-by: Mark Craske Signed-off-by: David S. Miller diff --git a/drivers/net/usb/asix.h b/drivers/net/usb/asix.h index 5d049d0..a2d3ea6 100644 --- a/drivers/net/usb/asix.h +++ b/drivers/net/usb/asix.h @@ -168,7 +168,7 @@ struct asix_data { struct asix_rx_fixup_info { struct sk_buff *ax_skb; u32 header; - u16 size; + u16 remaining; bool split_head; }; diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c index 75d6f26..2bd5bdd 100644 --- a/drivers/net/usb/asix_common.c +++ b/drivers/net/usb/asix_common.c @@ -54,12 +54,13 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, struct asix_rx_fixup_info *rx) { int offset = 0; + u16 size; while (offset + sizeof(u16) <= skb->len) { - u16 remaining = 0; + u16 copy_length; unsigned char *data; - if (!rx->size) { + if (!rx->remaining) { if ((skb->len - offset == sizeof(u16)) || rx->split_head) { if(!rx->split_head) { @@ -81,42 +82,42 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, offset += sizeof(u32); } - /* get the packet length */ - rx->size = (u16) (rx->header & 0x7ff); - if (rx->size != ((~rx->header >> 16) & 0x7ff)) { + /* take frame length from Data header 32-bit word */ + size = (u16)(rx->header & 0x7ff); + if (size != ((~rx->header >> 16) & 0x7ff)) { netdev_err(dev->net, "asix_rx_fixup() Bad Header Length 0x%x, offset %d\n", rx->header, offset); - rx->size = 0; return 0; } - rx->ax_skb = netdev_alloc_skb_ip_align(dev->net, - rx->size); + rx->ax_skb = netdev_alloc_skb_ip_align(dev->net, size); if (!rx->ax_skb) return 0; + rx->remaining = size; } - if (rx->size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) { + if (rx->remaining > dev->net->mtu + ETH_HLEN + VLAN_HLEN) { netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n", - rx->size); + rx->remaining); kfree_skb(rx->ax_skb); rx->ax_skb = NULL; - rx->size = 0U; - + rx->remaining = 0; return 0; } - if (rx->size > skb->len - offset) { - remaining = rx->size - (skb->len - offset); - rx->size = skb->len - offset; + if (rx->remaining > skb->len - offset) { + copy_length = skb->len - offset; + rx->remaining -= copy_length; + } else { + copy_length = rx->remaining; + rx->remaining = 0; } - data = skb_put(rx->ax_skb, rx->size); - memcpy(data, skb->data + offset, rx->size); - if (!remaining) + data = skb_put(rx->ax_skb, copy_length); + memcpy(data, skb->data + offset, copy_length); + if (!rx->remaining) usbnet_skb_return(dev, rx->ax_skb); - offset += (rx->size + 1) & 0xfffe; - rx->size = remaining; + offset += (copy_length + 1) & 0xfffe; } if (skb->len != offset) { -- cgit v0.10.2 From 3bfc69abf802f56901ffd83bb66b7dd7644ddcc3 Mon Sep 17 00:00:00 2001 From: Dean Jenkins Date: Fri, 2 Oct 2015 14:29:05 +0100 Subject: asix: Tidy-up 32-bit header word synchronisation Tidy-up the Data header 32-bit word synchronisation logic in asix_rx_fixup_internal() by removing redundant logic tests. The code is looking at the following cases of the Data header 32-bit word that is present before each Ethernet frame: a) all 32 bits of the Data header word are in the URB socket buffer b) first 16 bits of the Data header word are at the end of the URB socket buffer c) last 16 bits of the Data header word are at the start of the URB socket buffer eg. split_head = true Note that the lifetime of rx->split_head exists outside of the function call and is accessed per processing of each URB. Therefore, split_head being true acts on the next URB to be processed. To check for b) the offset will be 16 bits (2 bytes) from the end of the buffer then indicate split_head is true. To check for c) split_head must be true because the first 16 bits have been found. To check for a) else c) Note that the || logic of the old code included the state (skb->len - offset == sizeof(u16) && rx->split_head) which is not possible because the split_head cannot be true whilst checking for b). This is because the split_head indicates that the first 16 bits have been found and that is not possible whilst checking for the first 16 bits. Therefore simplify the logic. Signed-off-by: Dean Jenkins Signed-off-by: Mark Craske Signed-off-by: David S. Miller diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c index 2bd5bdd..89efd6a 100644 --- a/drivers/net/usb/asix_common.c +++ b/drivers/net/usb/asix_common.c @@ -61,21 +61,19 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, unsigned char *data; if (!rx->remaining) { - if ((skb->len - offset == sizeof(u16)) || - rx->split_head) { - if(!rx->split_head) { - rx->header = get_unaligned_le16( - skb->data + offset); - rx->split_head = true; - offset += sizeof(u16); - break; - } else { - rx->header |= (get_unaligned_le16( - skb->data + offset) - << 16); - rx->split_head = false; - offset += sizeof(u16); - } + if (skb->len - offset == sizeof(u16)) { + rx->header = get_unaligned_le16( + skb->data + offset); + rx->split_head = true; + offset += sizeof(u16); + break; + } + + if (rx->split_head == true) { + rx->header |= (get_unaligned_le16( + skb->data + offset) << 16); + rx->split_head = false; + offset += sizeof(u16); } else { rx->header = get_unaligned_le32(skb->data + offset); -- cgit v0.10.2 From 9a5ccd8e039eef53336e45d01c7d8a1acbd36b47 Mon Sep 17 00:00:00 2001 From: Dean Jenkins Date: Fri, 2 Oct 2015 14:29:06 +0100 Subject: asix: Simplify asix_rx_fixup_internal() netdev alloc The code is checking that the Ethernet frame will fit into a netdev allocated socket buffer within the constraints of MTU size, Ethernet header length plus VLAN header length. The original code was checking rx->remaining each loop of the while loop that processes multiple Ethernet frames per URB and/or Ethernet frames that span across URBs. rx->remaining decreases per while loop so there is no point in potentially checking multiple times that the Ethernet frame (remaining part) will fit into the netdev socket buffer. The modification checks that the size of the Ethernet frame will fit the netdev socket buffer before allocating the netdev socket buffer. This avoids grabbing memory and then deciding that the Ethernet frame is too big and then freeing the memory. Signed-off-by: Dean Jenkins Signed-off-by: Mark Craske Signed-off-by: David S. Miller diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c index 89efd6a..6a8eddf 100644 --- a/drivers/net/usb/asix_common.c +++ b/drivers/net/usb/asix_common.c @@ -87,19 +87,17 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, rx->header, offset); return 0; } + if (size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) { + netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n", + size); + return 0; + } + rx->ax_skb = netdev_alloc_skb_ip_align(dev->net, size); if (!rx->ax_skb) return 0; - rx->remaining = size; - } - if (rx->remaining > dev->net->mtu + ETH_HLEN + VLAN_HLEN) { - netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n", - rx->remaining); - kfree_skb(rx->ax_skb); - rx->ax_skb = NULL; - rx->remaining = 0; - return 0; + rx->remaining = size; } if (rx->remaining > skb->len - offset) { -- cgit v0.10.2 From 3f30b158eba5c604b6e0870027eef5d19fc9271d Mon Sep 17 00:00:00 2001 From: Dean Jenkins Date: Fri, 2 Oct 2015 14:29:07 +0100 Subject: asix: On RX avoid creating bad Ethernet frames When RX Ethernet frames span multiple URB socket buffers, the data stream may suffer a discontinuity which will cause the current Ethernet frame in the netdev socket buffer to be incomplete. This frame needs to be discarded instead of appending unrelated data from the current URB socket buffer to the Ethernet frame in the netdev socket buffer. This avoids creating a corrupted Ethernet frame in the netdev socket buffer. A discontinuity can occur when the previous URB socket buffer held an incomplete Ethernet frame due to truncation or a URB socket buffer containing the end of the Ethernet frame was missing. Therefore, add a sanity test for when an Ethernet frame spans multiple URB socket buffers to check that the remaining bytes of the currently received Ethernet frame point to a good Data header 32-bit word of the next Ethernet frame. Upon error, reset the remaining bytes variable to zero and discard the current netdev socket buffer. Assume that the Data header is located at the start of the current socket buffer and attempt to process the next Ethernet frame from there. This avoids unnecessarily discarding a good URB socket buffer that contains a new Ethernet frame. Signed-off-by: Dean Jenkins Signed-off-by: Mark Craske Signed-off-by: David S. Miller diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c index 6a8eddf..1e4768d 100644 --- a/drivers/net/usb/asix_common.c +++ b/drivers/net/usb/asix_common.c @@ -56,6 +56,34 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, int offset = 0; u16 size; + /* When an Ethernet frame spans multiple URB socket buffers, + * do a sanity test for the Data header synchronisation. + * Attempt to detect the situation of the previous socket buffer having + * been truncated or a socket buffer was missing. These situations + * cause a discontinuity in the data stream and therefore need to avoid + * appending bad data to the end of the current netdev socket buffer. + * Also avoid unnecessarily discarding a good current netdev socket + * buffer. + */ + if (rx->remaining && (rx->remaining + sizeof(u32) <= skb->len)) { + offset = ((rx->remaining + 1) & 0xfffe) + sizeof(u32); + rx->header = get_unaligned_le32(skb->data + offset); + offset = 0; + + size = (u16)(rx->header & 0x7ff); + if (size != ((~rx->header >> 16) & 0x7ff)) { + netdev_err(dev->net, "asix_rx_fixup() Data Header synchronisation was lost, remaining %d\n", + rx->remaining); + kfree_skb(rx->ax_skb); + rx->ax_skb = NULL; + /* Discard the incomplete netdev Ethernet frame and + * assume the Data header is at the start of the current + * URB socket buffer. + */ + rx->remaining = 0; + } + } + while (offset + sizeof(u16) <= skb->len) { u16 copy_length; unsigned char *data; -- cgit v0.10.2 From 6a570814cd430fa5ef4f278e8046dcf12ee63f13 Mon Sep 17 00:00:00 2001 From: Dean Jenkins Date: Fri, 2 Oct 2015 14:29:08 +0100 Subject: asix: Continue processing URB if no RX netdev buffer Avoid a loss of synchronisation of the Ethernet Data header 32-bit word due to a failure to get a netdev socket buffer. The ASIX RX handling algorithm returned 0 upon a failure to get an allocation of a netdev socket buffer. This causes the URB processing to stop which potentially causes a loss of synchronisation with the Ethernet Data header 32-bit word. Therefore, subsequent processing of URBs may be rejected due to a loss of synchronisation. This may cause additional good Ethernet frames to be discarded along with outputting of synchronisation error messages. Implement a solution which checks whether a netdev socket buffer has been allocated before trying to copy the Ethernet frame into the netdev socket buffer. But continue to process the URB so that synchronisation is maintained. Therefore, only a single Ethernet frame is discarded when no netdev socket buffer is available. Signed-off-by: Dean Jenkins Signed-off-by: Mark Craske Signed-off-by: David S. Miller diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c index 1e4768d..a186b0a 100644 --- a/drivers/net/usb/asix_common.c +++ b/drivers/net/usb/asix_common.c @@ -74,12 +74,14 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, if (size != ((~rx->header >> 16) & 0x7ff)) { netdev_err(dev->net, "asix_rx_fixup() Data Header synchronisation was lost, remaining %d\n", rx->remaining); - kfree_skb(rx->ax_skb); - rx->ax_skb = NULL; - /* Discard the incomplete netdev Ethernet frame and - * assume the Data header is at the start of the current - * URB socket buffer. - */ + if (rx->ax_skb) { + kfree_skb(rx->ax_skb); + rx->ax_skb = NULL; + /* Discard the incomplete netdev Ethernet frame + * and assume the Data header is at the start of + * the current URB socket buffer. + */ + } rx->remaining = 0; } } @@ -121,9 +123,12 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, return 0; } + /* Sometimes may fail to get a netdev socket buffer but + * continue to process the URB socket buffer so that + * synchronisation of the Ethernet frame Data header + * word is maintained. + */ rx->ax_skb = netdev_alloc_skb_ip_align(dev->net, size); - if (!rx->ax_skb) - return 0; rx->remaining = size; } @@ -136,10 +141,12 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, rx->remaining = 0; } - data = skb_put(rx->ax_skb, copy_length); - memcpy(data, skb->data + offset, copy_length); - if (!rx->remaining) - usbnet_skb_return(dev, rx->ax_skb); + if (rx->ax_skb) { + data = skb_put(rx->ax_skb, copy_length); + memcpy(data, skb->data + offset, copy_length); + if (!rx->remaining) + usbnet_skb_return(dev, rx->ax_skb); + } offset += (copy_length + 1) & 0xfffe; } -- cgit v0.10.2 From 4560cdff03a76348ee5fae48e3c7914e4de2db5b Mon Sep 17 00:00:00 2001 From: Nicolas Schichan Date: Fri, 2 Oct 2015 17:06:47 +0200 Subject: ARM: net: support BPF_ALU | BPF_MOD instructions in the BPF JIT. For ARMv7 with UDIV instruction support, generate an UDIV instruction followed by an MLS instruction. For other ARM variants, generate code calling a C wrapper similar to the jit_udiv() function used for BPF_ALU | BPF_DIV instructions. Some performance numbers reported by the test_bpf module (the duration per filter run is reported in nanoseconds, between "jitted:" and "PASS": ARMv7 QEMU nojit: test_bpf: #3 DIV_MOD_KX jited:0 2196 PASS ARMv7 QEMU jit: test_bpf: #3 DIV_MOD_KX jited:1 104 PASS ARMv5 QEMU nojit: test_bpf: #3 DIV_MOD_KX jited:0 2176 PASS ARMv5 QEMU jit: test_bpf: #3 DIV_MOD_KX jited:1 1104 PASS ARMv5 kirkwood nojit: test_bpf: #3 DIV_MOD_KX jited:0 1103 PASS ARMv5 kirkwood jit: test_bpf: #3 DIV_MOD_KX jited:1 311 PASS Signed-off-by: Nicolas Schichan Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller diff --git a/arch/arm/net/bpf_jit_32.c b/arch/arm/net/bpf_jit_32.c index 0df5fd5..6be4151 100644 --- a/arch/arm/net/bpf_jit_32.c +++ b/arch/arm/net/bpf_jit_32.c @@ -125,7 +125,7 @@ static u64 jit_get_skb_w(struct sk_buff *skb, int offset) } /* - * Wrapper that handles both OABI and EABI and assures Thumb2 interworking + * Wrappers which handle both OABI and EABI and assures Thumb2 interworking * (where the assembly routines like __aeabi_uidiv could cause problems). */ static u32 jit_udiv(u32 dividend, u32 divisor) @@ -133,6 +133,11 @@ static u32 jit_udiv(u32 dividend, u32 divisor) return dividend / divisor; } +static u32 jit_mod(u32 dividend, u32 divisor) +{ + return dividend % divisor; +} + static inline void _emit(int cond, u32 inst, struct jit_ctx *ctx) { inst |= (cond << 28); @@ -471,11 +476,17 @@ static inline void emit_blx_r(u8 tgt_reg, struct jit_ctx *ctx) #endif } -static inline void emit_udiv(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx) +static inline void emit_udivmod(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx, + int bpf_op) { #if __LINUX_ARM_ARCH__ == 7 if (elf_hwcap & HWCAP_IDIVA) { - emit(ARM_UDIV(rd, rm, rn), ctx); + if (bpf_op == BPF_DIV) + emit(ARM_UDIV(rd, rm, rn), ctx); + else { + emit(ARM_UDIV(ARM_R3, rm, rn), ctx); + emit(ARM_MLS(rd, rn, ARM_R3, rm), ctx); + } return; } #endif @@ -496,7 +507,8 @@ static inline void emit_udiv(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx) emit(ARM_MOV_R(ARM_R0, rm), ctx); ctx->seen |= SEEN_CALL; - emit_mov_i(ARM_R3, (u32)jit_udiv, ctx); + emit_mov_i(ARM_R3, bpf_op == BPF_DIV ? (u32)jit_udiv : (u32)jit_mod, + ctx); emit_blx_r(ARM_R3, ctx); if (rd != ARM_R0) @@ -697,13 +709,27 @@ load_ind: if (k == 1) break; emit_mov_i(r_scratch, k, ctx); - emit_udiv(r_A, r_A, r_scratch, ctx); + emit_udivmod(r_A, r_A, r_scratch, ctx, BPF_DIV); break; case BPF_ALU | BPF_DIV | BPF_X: update_on_xread(ctx); emit(ARM_CMP_I(r_X, 0), ctx); emit_err_ret(ARM_COND_EQ, ctx); - emit_udiv(r_A, r_A, r_X, ctx); + emit_udivmod(r_A, r_A, r_X, ctx, BPF_DIV); + break; + case BPF_ALU | BPF_MOD | BPF_K: + if (k == 1) { + emit_mov_i(r_A, 0, ctx); + break; + } + emit_mov_i(r_scratch, k, ctx); + emit_udivmod(r_A, r_A, r_scratch, ctx, BPF_MOD); + break; + case BPF_ALU | BPF_MOD | BPF_X: + update_on_xread(ctx); + emit(ARM_CMP_I(r_X, 0), ctx); + emit_err_ret(ARM_COND_EQ, ctx); + emit_udivmod(r_A, r_A, r_X, ctx, BPF_MOD); break; case BPF_ALU | BPF_OR | BPF_K: /* A |= K */ diff --git a/arch/arm/net/bpf_jit_32.h b/arch/arm/net/bpf_jit_32.h index 4b17d5ab..c46fca2 100644 --- a/arch/arm/net/bpf_jit_32.h +++ b/arch/arm/net/bpf_jit_32.h @@ -115,6 +115,8 @@ #define ARM_INST_UMULL 0x00800090 +#define ARM_INST_MLS 0x00600090 + /* * Use a suitable undefined instruction to use for ARM/Thumb2 faulting. * We need to be careful not to conflict with those used by other modules @@ -210,4 +212,7 @@ #define ARM_UMULL(rd_lo, rd_hi, rn, rm) (ARM_INST_UMULL | (rd_hi) << 16 \ | (rd_lo) << 12 | (rm) << 8 | rn) +#define ARM_MLS(rd, rn, rm, ra) (ARM_INST_MLS | (rd) << 16 | (rn) | (rm) << 8 \ + | (ra) << 12) + #endif /* PFILTER_OPCODES_ARM_H */ -- cgit v0.10.2 From 0cdf5640e4f6940bdbbefee4bb0adb7dffb185ec Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 2 Oct 2015 18:42:00 +0200 Subject: ebpf: include perf_event only where really needed Commit ea317b267e9d ("bpf: Add new bpf map type to store the pointer to struct perf_event") added perf_event.h to the main eBPF header, so it gets included for all users. perf_event.h is actually only needed from array map side, so lets sanitize this a bit. Signed-off-by: Daniel Borkmann Cc: Kaixu Xia Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller diff --git a/include/linux/bpf.h b/include/linux/bpf.h index f57d7fe..c915a6b 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -10,7 +10,6 @@ #include #include #include -#include struct bpf_map; diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index 29ace10..2fecc4a 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -15,6 +15,7 @@ #include #include #include +#include /* Called from syscall */ static struct bpf_map *array_map_alloc(union bpf_attr *attr) -- cgit v0.10.2 From 5edfcee5ed73eb9537987c4ddb6bf062b6943b73 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Fri, 25 Sep 2015 08:42:00 +0200 Subject: mac80211: make ieee80211_new_mesh_header return unsigned The function returns always non-negative values. The problem has been detected using proposed semantic patch scripts/coccinelle/tests/assign_signed_to_unsigned.cocci [1]. [1]: http://permalink.gmane.org/gmane.linux.kernel/2046107 Signed-off-by: Andrzej Hajda Signed-off-by: Johannes Berg diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 62b3e29..626e8de 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -606,9 +606,9 @@ int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc, * * Return the header length. */ -int ieee80211_new_mesh_header(struct ieee80211_sub_if_data *sdata, - struct ieee80211s_hdr *meshhdr, - const char *addr4or5, const char *addr6) +unsigned int ieee80211_new_mesh_header(struct ieee80211_sub_if_data *sdata, + struct ieee80211s_hdr *meshhdr, + const char *addr4or5, const char *addr6) { if (WARN_ON(!addr4or5 && addr6)) return 0; diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index c60be85..a159634 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -207,9 +207,9 @@ struct mesh_rmc { /* Various */ int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc, const u8 *da, const u8 *sa); -int ieee80211_new_mesh_header(struct ieee80211_sub_if_data *sdata, - struct ieee80211s_hdr *meshhdr, - const char *addr4or5, const char *addr6); +unsigned int ieee80211_new_mesh_header(struct ieee80211_sub_if_data *sdata, + struct ieee80211s_hdr *meshhdr, + const char *addr4or5, const char *addr6); int mesh_rmc_check(struct ieee80211_sub_if_data *sdata, const u8 *addr, struct ieee80211s_hdr *mesh_hdr); bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, -- cgit v0.10.2 From 4bebdd7a4d2960b2ff6c40b27156d041ea270765 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Thu, 10 Sep 2015 11:57:14 -0700 Subject: RDS: defer the over_batch work to send worker Current process gives up if its send work over the batch limit. The work queue will get kicked to finish off any other requests. This fixes remainder condition from commit 443be0e5affe ("RDS: make sure not to loop forever inside rds_send_xmit"). The restart condition is only for the case where we reached to over_batch code for some other reason so just retrying again before giving up. While at it, make sure we use already available 'send_batch_count' parameter instead of magic value. The batch count threshold value of 1024 came via commit 443be0e5affe ("RDS: make sure not to loop forever inside rds_send_xmit"). The idea is to process as big a batch as we can but at the same time we don't hold other waiting processes for send. Hence back-off after the send_batch_count limit (1024) to avoid soft-lock ups. Signed-off-by: Santosh Shilimkar Signed-off-by: Santosh Shilimkar diff --git a/net/rds/send.c b/net/rds/send.c index 4df61a5..b0acd45 100644 --- a/net/rds/send.c +++ b/net/rds/send.c @@ -38,6 +38,7 @@ #include #include #include +#include #include "rds.h" @@ -51,7 +52,7 @@ * it to 0 will restore the old behavior (where we looped until we had * drained the queue). */ -static int send_batch_count = 64; +static int send_batch_count = SZ_1K; module_param(send_batch_count, int, 0444); MODULE_PARM_DESC(send_batch_count, " batch factor when working the send queue"); @@ -223,7 +224,7 @@ restart: * through a lot of messages, lets back off and see * if anyone else jumps in */ - if (batch_count >= 1024) + if (batch_count >= send_batch_count) goto over_batch; spin_lock_irqsave(&conn->c_lock, flags); @@ -423,7 +424,9 @@ over_batch: !list_empty(&conn->c_send_queue)) && send_gen == conn->c_send_gen) { rds_stats_inc(s_send_lock_queue_raced); - goto restart; + if (batch_count < send_batch_count) + goto restart; + queue_delayed_work(rds_wq, &conn->c_send_w, 1); } } out: -- cgit v0.10.2 From db6526dcb51b054961a2d96ba43dec23e38818b3 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Fri, 11 Sep 2015 15:44:29 -0700 Subject: RDS: use rds_send_xmit() state instead of RDS_LL_SEND_FULL In Transport indepedent rds_sendmsg(), we shouldn't make decisions based on RDS_LL_SEND_FULL which is used to manage the ring for RDMA based transports. We can safely issue rds_send_xmit() and the using its return value take decision on deferred work. This will also fix the scenario where at times we are seeing connections stuck with the LL_SEND_FULL bit getting set and never cleared. We kick krdsd after any time we see -ENOMEM or -EAGAIN from the ring allocation code. Signed-off-by: Santosh Shilimkar Signed-off-by: Santosh Shilimkar diff --git a/net/rds/send.c b/net/rds/send.c index b0acd45..a081a64 100644 --- a/net/rds/send.c +++ b/net/rds/send.c @@ -1123,8 +1123,9 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len) */ rds_stats_inc(s_send_queued); - if (!test_bit(RDS_LL_SEND_FULL, &conn->c_flags)) - rds_send_xmit(conn); + ret = rds_send_xmit(conn); + if (ret == -ENOMEM || ret == -EAGAIN) + queue_delayed_work(rds_wq, &conn->c_send_w, 1); rds_message_put(rm); return payload_len; @@ -1180,8 +1181,9 @@ rds_send_pong(struct rds_connection *conn, __be16 dport) rds_stats_inc(s_send_queued); rds_stats_inc(s_send_pong); - if (!test_bit(RDS_LL_SEND_FULL, &conn->c_flags)) - queue_delayed_work(rds_wq, &conn->c_send_w, 0); + ret = rds_send_xmit(conn); + if (ret == -ENOMEM || ret == -EAGAIN) + queue_delayed_work(rds_wq, &conn->c_send_w, 1); rds_message_put(rm); return 0; diff --git a/net/rds/threads.c b/net/rds/threads.c index dc2402e..454aa6d 100644 --- a/net/rds/threads.c +++ b/net/rds/threads.c @@ -162,7 +162,9 @@ void rds_send_worker(struct work_struct *work) int ret; if (rds_conn_state(conn) == RDS_CONN_UP) { + clear_bit(RDS_LL_SEND_FULL, &conn->c_flags); ret = rds_send_xmit(conn); + cond_resched(); rdsdebug("conn %p ret %d\n", conn, ret); switch (ret) { case -EAGAIN: -- cgit v0.10.2 From f4f943c958a2869b0601092857c1cf0e485d3ce8 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Sun, 6 Sep 2015 02:18:51 -0400 Subject: RDS: IB: ack more receive completions to improve performance For better performance, we split the receive completion IRQ handler. That lets us acknowledge several WCE events in one call. We also limit the WC to max 32 to avoid latency. Acknowledging several completions in one call instead of several calls each time will provide better performance since less mutual exclusion locks are being performed. In next patch, send completion is also split which re-uses the poll_cq() and hence the code is moved to ib_cm.c Signed-off-by: Santosh Shilimkar Signed-off-by: Santosh Shilimkar diff --git a/net/rds/ib.h b/net/rds/ib.h index f1fd5ff..727759b 100644 --- a/net/rds/ib.h +++ b/net/rds/ib.h @@ -24,6 +24,8 @@ #define RDS_IB_RECYCLE_BATCH_COUNT 32 +#define RDS_IB_WC_MAX 32 + extern struct rw_semaphore rds_ib_devices_lock; extern struct list_head rds_ib_devices; @@ -89,6 +91,20 @@ struct rds_ib_work_ring { atomic_t w_free_ctr; }; +/* Rings are posted with all the allocations they'll need to queue the + * incoming message to the receiving socket so this can't fail. + * All fragments start with a header, so we can make sure we're not receiving + * garbage, and we can tell a small 8 byte fragment from an ACK frame. + */ +struct rds_ib_ack_state { + u64 ack_next; + u64 ack_recv; + unsigned int ack_required:1; + unsigned int ack_next_valid:1; + unsigned int ack_recv_valid:1; +}; + + struct rds_ib_device; struct rds_ib_connection { @@ -102,6 +118,10 @@ struct rds_ib_connection { struct ib_pd *i_pd; struct ib_cq *i_send_cq; struct ib_cq *i_recv_cq; + struct ib_wc i_recv_wc[RDS_IB_WC_MAX]; + + /* interrupt handling */ + struct tasklet_struct i_recv_tasklet; /* tx */ struct rds_ib_work_ring i_send_ring; @@ -112,7 +132,6 @@ struct rds_ib_connection { atomic_t i_signaled_sends; /* rx */ - struct tasklet_struct i_recv_tasklet; struct mutex i_recv_mutex; struct rds_ib_work_ring i_recv_ring; struct rds_ib_incoming *i_ibinc; @@ -199,13 +218,14 @@ struct rds_ib_statistics { uint64_t s_ib_connect_raced; uint64_t s_ib_listen_closed_stale; uint64_t s_ib_tx_cq_call; + uint64_t s_ib_evt_handler_call; + uint64_t s_ib_tasklet_call; uint64_t s_ib_tx_cq_event; uint64_t s_ib_tx_ring_full; uint64_t s_ib_tx_throttle; uint64_t s_ib_tx_sg_mapping_failure; uint64_t s_ib_tx_stalled; uint64_t s_ib_tx_credit_updates; - uint64_t s_ib_rx_cq_call; uint64_t s_ib_rx_cq_event; uint64_t s_ib_rx_ring_empty; uint64_t s_ib_rx_refill_from_cq; @@ -324,7 +344,8 @@ void rds_ib_recv_free_caches(struct rds_ib_connection *ic); void rds_ib_recv_refill(struct rds_connection *conn, int prefill, gfp_t gfp); void rds_ib_inc_free(struct rds_incoming *inc); int rds_ib_inc_copy_to_user(struct rds_incoming *inc, struct iov_iter *to); -void rds_ib_recv_cq_comp_handler(struct ib_cq *cq, void *context); +void rds_ib_recv_cqe_handler(struct rds_ib_connection *ic, struct ib_wc *wc, + struct rds_ib_ack_state *state); void rds_ib_recv_tasklet_fn(unsigned long data); void rds_ib_recv_init_ring(struct rds_ib_connection *ic); void rds_ib_recv_clear_ring(struct rds_ib_connection *ic); @@ -332,6 +353,7 @@ void rds_ib_recv_init_ack(struct rds_ib_connection *ic); void rds_ib_attempt_ack(struct rds_ib_connection *ic); void rds_ib_ack_send_complete(struct rds_ib_connection *ic); u64 rds_ib_piggyb_ack(struct rds_ib_connection *ic); +void rds_ib_set_ack(struct rds_ib_connection *ic, u64 seq, int ack_required); /* ib_ring.c */ void rds_ib_ring_init(struct rds_ib_work_ring *ring, u32 nr); diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c index 9043f5c..28e0979 100644 --- a/net/rds/ib_cm.c +++ b/net/rds/ib_cm.c @@ -216,6 +216,72 @@ static void rds_ib_cq_event_handler(struct ib_event *event, void *data) event->event, ib_event_msg(event->event), data); } +/* Plucking the oldest entry from the ring can be done concurrently with + * the thread refilling the ring. Each ring operation is protected by + * spinlocks and the transient state of refilling doesn't change the + * recording of which entry is oldest. + * + * This relies on IB only calling one cq comp_handler for each cq so that + * there will only be one caller of rds_recv_incoming() per RDS connection. + */ +static void rds_ib_cq_comp_handler_recv(struct ib_cq *cq, void *context) +{ + struct rds_connection *conn = context; + struct rds_ib_connection *ic = conn->c_transport_data; + + rdsdebug("conn %p cq %p\n", conn, cq); + + rds_ib_stats_inc(s_ib_evt_handler_call); + + tasklet_schedule(&ic->i_recv_tasklet); +} + +static void poll_cq(struct rds_ib_connection *ic, struct ib_cq *cq, + struct ib_wc *wcs, + struct rds_ib_ack_state *ack_state) +{ + int nr; + int i; + struct ib_wc *wc; + + while ((nr = ib_poll_cq(cq, RDS_IB_WC_MAX, wcs)) > 0) { + for (i = 0; i < nr; i++) { + wc = wcs + i; + rdsdebug("wc wr_id 0x%llx status %u byte_len %u imm_data %u\n", + (unsigned long long)wc->wr_id, wc->status, + wc->byte_len, be32_to_cpu(wc->ex.imm_data)); + rds_ib_recv_cqe_handler(ic, wc, ack_state); + } + } +} + +static void rds_ib_tasklet_fn_recv(unsigned long data) +{ + struct rds_ib_connection *ic = (struct rds_ib_connection *)data; + struct rds_connection *conn = ic->conn; + struct rds_ib_device *rds_ibdev = ic->rds_ibdev; + struct rds_ib_ack_state state; + + BUG_ON(!rds_ibdev); + + rds_ib_stats_inc(s_ib_tasklet_call); + + memset(&state, 0, sizeof(state)); + poll_cq(ic, ic->i_recv_cq, ic->i_recv_wc, &state); + ib_req_notify_cq(ic->i_recv_cq, IB_CQ_SOLICITED); + poll_cq(ic, ic->i_recv_cq, ic->i_recv_wc, &state); + + if (state.ack_next_valid) + rds_ib_set_ack(ic, state.ack_next, state.ack_required); + if (state.ack_recv_valid && state.ack_recv > ic->i_ack_recv) { + rds_send_drop_acked(conn, state.ack_recv, NULL); + ic->i_ack_recv = state.ack_recv; + } + + if (rds_conn_up(conn)) + rds_ib_attempt_ack(ic); +} + static void rds_ib_qp_event_handler(struct ib_event *event, void *data) { struct rds_connection *conn = data; @@ -282,7 +348,7 @@ static int rds_ib_setup_qp(struct rds_connection *conn) } cq_attr.cqe = ic->i_recv_ring.w_nr; - ic->i_recv_cq = ib_create_cq(dev, rds_ib_recv_cq_comp_handler, + ic->i_recv_cq = ib_create_cq(dev, rds_ib_cq_comp_handler_recv, rds_ib_cq_event_handler, conn, &cq_attr); if (IS_ERR(ic->i_recv_cq)) { @@ -743,7 +809,7 @@ int rds_ib_conn_alloc(struct rds_connection *conn, gfp_t gfp) } INIT_LIST_HEAD(&ic->ib_node); - tasklet_init(&ic->i_recv_tasklet, rds_ib_recv_tasklet_fn, + tasklet_init(&ic->i_recv_tasklet, rds_ib_tasklet_fn_recv, (unsigned long) ic); mutex_init(&ic->i_recv_mutex); #ifndef KERNEL_HAS_ATOMIC64 diff --git a/net/rds/ib_recv.c b/net/rds/ib_recv.c index f43831e..96744b7 100644 --- a/net/rds/ib_recv.c +++ b/net/rds/ib_recv.c @@ -596,8 +596,7 @@ void rds_ib_recv_init_ack(struct rds_ib_connection *ic) * wr_id and avoids working with the ring in that case. */ #ifndef KERNEL_HAS_ATOMIC64 -static void rds_ib_set_ack(struct rds_ib_connection *ic, u64 seq, - int ack_required) +void rds_ib_set_ack(struct rds_ib_connection *ic, u64 seq, int ack_required) { unsigned long flags; @@ -622,8 +621,7 @@ static u64 rds_ib_get_ack(struct rds_ib_connection *ic) return seq; } #else -static void rds_ib_set_ack(struct rds_ib_connection *ic, u64 seq, - int ack_required) +void rds_ib_set_ack(struct rds_ib_connection *ic, u64 seq, int ack_required) { atomic64_set(&ic->i_ack_next, seq); if (ack_required) { @@ -830,20 +828,6 @@ static void rds_ib_cong_recv(struct rds_connection *conn, rds_cong_map_updated(map, uncongested); } -/* - * Rings are posted with all the allocations they'll need to queue the - * incoming message to the receiving socket so this can't fail. - * All fragments start with a header, so we can make sure we're not receiving - * garbage, and we can tell a small 8 byte fragment from an ACK frame. - */ -struct rds_ib_ack_state { - u64 ack_next; - u64 ack_recv; - unsigned int ack_required:1; - unsigned int ack_next_valid:1; - unsigned int ack_recv_valid:1; -}; - static void rds_ib_process_recv(struct rds_connection *conn, struct rds_ib_recv_work *recv, u32 data_len, struct rds_ib_ack_state *state) @@ -969,96 +953,50 @@ static void rds_ib_process_recv(struct rds_connection *conn, } } -/* - * Plucking the oldest entry from the ring can be done concurrently with - * the thread refilling the ring. Each ring operation is protected by - * spinlocks and the transient state of refilling doesn't change the - * recording of which entry is oldest. - * - * This relies on IB only calling one cq comp_handler for each cq so that - * there will only be one caller of rds_recv_incoming() per RDS connection. - */ -void rds_ib_recv_cq_comp_handler(struct ib_cq *cq, void *context) -{ - struct rds_connection *conn = context; - struct rds_ib_connection *ic = conn->c_transport_data; - - rdsdebug("conn %p cq %p\n", conn, cq); - - rds_ib_stats_inc(s_ib_rx_cq_call); - - tasklet_schedule(&ic->i_recv_tasklet); -} - -static inline void rds_poll_cq(struct rds_ib_connection *ic, - struct rds_ib_ack_state *state) +void rds_ib_recv_cqe_handler(struct rds_ib_connection *ic, + struct ib_wc *wc, + struct rds_ib_ack_state *state) { struct rds_connection *conn = ic->conn; - struct ib_wc wc; struct rds_ib_recv_work *recv; - while (ib_poll_cq(ic->i_recv_cq, 1, &wc) > 0) { - rdsdebug("wc wr_id 0x%llx status %u (%s) byte_len %u imm_data %u\n", - (unsigned long long)wc.wr_id, wc.status, - ib_wc_status_msg(wc.status), wc.byte_len, - be32_to_cpu(wc.ex.imm_data)); - rds_ib_stats_inc(s_ib_rx_cq_event); + rdsdebug("wc wr_id 0x%llx status %u (%s) byte_len %u imm_data %u\n", + (unsigned long long)wc->wr_id, wc->status, + ib_wc_status_msg(wc->status), wc->byte_len, + be32_to_cpu(wc->ex.imm_data)); - recv = &ic->i_recvs[rds_ib_ring_oldest(&ic->i_recv_ring)]; - - ib_dma_unmap_sg(ic->i_cm_id->device, &recv->r_frag->f_sg, 1, DMA_FROM_DEVICE); - - /* - * Also process recvs in connecting state because it is possible - * to get a recv completion _before_ the rdmacm ESTABLISHED - * event is processed. - */ - if (wc.status == IB_WC_SUCCESS) { - rds_ib_process_recv(conn, recv, wc.byte_len, state); - } else { - /* We expect errors as the qp is drained during shutdown */ - if (rds_conn_up(conn) || rds_conn_connecting(conn)) - rds_ib_conn_error(conn, "recv completion on %pI4 had " - "status %u (%s), disconnecting and " - "reconnecting\n", &conn->c_faddr, - wc.status, - ib_wc_status_msg(wc.status)); - } + rds_ib_stats_inc(s_ib_rx_cq_event); + recv = &ic->i_recvs[rds_ib_ring_oldest(&ic->i_recv_ring)]; + ib_dma_unmap_sg(ic->i_cm_id->device, &recv->r_frag->f_sg, 1, + DMA_FROM_DEVICE); - /* - * rds_ib_process_recv() doesn't always consume the frag, and - * we might not have called it at all if the wc didn't indicate - * success. We already unmapped the frag's pages, though, and - * the following rds_ib_ring_free() call tells the refill path - * that it will not find an allocated frag here. Make sure we - * keep that promise by freeing a frag that's still on the ring. - */ - if (recv->r_frag) { - rds_ib_frag_free(ic, recv->r_frag); - recv->r_frag = NULL; - } - rds_ib_ring_free(&ic->i_recv_ring, 1); + /* Also process recvs in connecting state because it is possible + * to get a recv completion _before_ the rdmacm ESTABLISHED + * event is processed. + */ + if (wc->status == IB_WC_SUCCESS) { + rds_ib_process_recv(conn, recv, wc->byte_len, state); + } else { + /* We expect errors as the qp is drained during shutdown */ + if (rds_conn_up(conn) || rds_conn_connecting(conn)) + rds_ib_conn_error(conn, "recv completion on %pI4 had status %u (%s), disconnecting and reconnecting\n", + &conn->c_faddr, + wc->status, + ib_wc_status_msg(wc->status)); } -} -void rds_ib_recv_tasklet_fn(unsigned long data) -{ - struct rds_ib_connection *ic = (struct rds_ib_connection *) data; - struct rds_connection *conn = ic->conn; - struct rds_ib_ack_state state = { 0, }; - - rds_poll_cq(ic, &state); - ib_req_notify_cq(ic->i_recv_cq, IB_CQ_SOLICITED); - rds_poll_cq(ic, &state); - - if (state.ack_next_valid) - rds_ib_set_ack(ic, state.ack_next, state.ack_required); - if (state.ack_recv_valid && state.ack_recv > ic->i_ack_recv) { - rds_send_drop_acked(conn, state.ack_recv, NULL); - ic->i_ack_recv = state.ack_recv; + /* rds_ib_process_recv() doesn't always consume the frag, and + * we might not have called it at all if the wc didn't indicate + * success. We already unmapped the frag's pages, though, and + * the following rds_ib_ring_free() call tells the refill path + * that it will not find an allocated frag here. Make sure we + * keep that promise by freeing a frag that's still on the ring. + */ + if (recv->r_frag) { + rds_ib_frag_free(ic, recv->r_frag); + recv->r_frag = NULL; } - if (rds_conn_up(conn)) - rds_ib_attempt_ack(ic); + rds_ib_ring_free(&ic->i_recv_ring, 1); /* If we ever end up with a really empty receive ring, we're * in deep trouble, as the sender will definitely see RNR diff --git a/net/rds/ib_stats.c b/net/rds/ib_stats.c index 2d5965d..bdf6115 100644 --- a/net/rds/ib_stats.c +++ b/net/rds/ib_stats.c @@ -42,14 +42,15 @@ DEFINE_PER_CPU_SHARED_ALIGNED(struct rds_ib_statistics, rds_ib_stats); static const char *const rds_ib_stat_names[] = { "ib_connect_raced", "ib_listen_closed_stale", + "s_ib_evt_handler_call", "ib_tx_cq_call", + "ib_tasklet_call", "ib_tx_cq_event", "ib_tx_ring_full", "ib_tx_throttle", "ib_tx_sg_mapping_failure", "ib_tx_stalled", "ib_tx_credit_updates", - "ib_rx_cq_call", "ib_rx_cq_event", "ib_rx_ring_empty", "ib_rx_refill_from_cq", -- cgit v0.10.2 From 0c28c04500cf956c82d542c199f5bddabd590af3 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Sun, 6 Sep 2015 02:18:51 -0400 Subject: RDS: IB: split send completion handling and do batch ack Similar to what we did with receive CQ completion handling, we split the transmit completion handler so that it lets us implement batched work completion handling. We re-use the cq_poll routine and makes use of RDS_IB_SEND_OP to identify the send vs receive completion event handler invocation. Signed-off-by: Santosh Shilimkar Signed-off-by: Santosh Shilimkar diff --git a/net/rds/ib.h b/net/rds/ib.h index 727759b..3a8cd31 100644 --- a/net/rds/ib.h +++ b/net/rds/ib.h @@ -25,6 +25,7 @@ #define RDS_IB_RECYCLE_BATCH_COUNT 32 #define RDS_IB_WC_MAX 32 +#define RDS_IB_SEND_OP BIT_ULL(63) extern struct rw_semaphore rds_ib_devices_lock; extern struct list_head rds_ib_devices; @@ -118,9 +119,11 @@ struct rds_ib_connection { struct ib_pd *i_pd; struct ib_cq *i_send_cq; struct ib_cq *i_recv_cq; + struct ib_wc i_send_wc[RDS_IB_WC_MAX]; struct ib_wc i_recv_wc[RDS_IB_WC_MAX]; /* interrupt handling */ + struct tasklet_struct i_send_tasklet; struct tasklet_struct i_recv_tasklet; /* tx */ @@ -217,7 +220,6 @@ struct rds_ib_device { struct rds_ib_statistics { uint64_t s_ib_connect_raced; uint64_t s_ib_listen_closed_stale; - uint64_t s_ib_tx_cq_call; uint64_t s_ib_evt_handler_call; uint64_t s_ib_tasklet_call; uint64_t s_ib_tx_cq_event; @@ -371,7 +373,7 @@ extern wait_queue_head_t rds_ib_ring_empty_wait; void rds_ib_xmit_complete(struct rds_connection *conn); int rds_ib_xmit(struct rds_connection *conn, struct rds_message *rm, unsigned int hdr_off, unsigned int sg, unsigned int off); -void rds_ib_send_cq_comp_handler(struct ib_cq *cq, void *context); +void rds_ib_send_cqe_handler(struct rds_ib_connection *ic, struct ib_wc *wc); void rds_ib_send_init_ring(struct rds_ib_connection *ic); void rds_ib_send_clear_ring(struct rds_ib_connection *ic); int rds_ib_xmit_rdma(struct rds_connection *conn, struct rm_rdma_op *op); diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c index 28e0979..8f51d0d 100644 --- a/net/rds/ib_cm.c +++ b/net/rds/ib_cm.c @@ -250,11 +250,34 @@ static void poll_cq(struct rds_ib_connection *ic, struct ib_cq *cq, rdsdebug("wc wr_id 0x%llx status %u byte_len %u imm_data %u\n", (unsigned long long)wc->wr_id, wc->status, wc->byte_len, be32_to_cpu(wc->ex.imm_data)); - rds_ib_recv_cqe_handler(ic, wc, ack_state); + + if (wc->wr_id & RDS_IB_SEND_OP) + rds_ib_send_cqe_handler(ic, wc); + else + rds_ib_recv_cqe_handler(ic, wc, ack_state); } } } +static void rds_ib_tasklet_fn_send(unsigned long data) +{ + struct rds_ib_connection *ic = (struct rds_ib_connection *)data; + struct rds_connection *conn = ic->conn; + struct rds_ib_ack_state state; + + rds_ib_stats_inc(s_ib_tasklet_call); + + memset(&state, 0, sizeof(state)); + poll_cq(ic, ic->i_send_cq, ic->i_send_wc, &state); + ib_req_notify_cq(ic->i_send_cq, IB_CQ_NEXT_COMP); + poll_cq(ic, ic->i_send_cq, ic->i_send_wc, &state); + + if (rds_conn_up(conn) && + (!test_bit(RDS_LL_SEND_FULL, &conn->c_flags) || + test_bit(0, &conn->c_map_queued))) + rds_send_xmit(ic->conn); +} + static void rds_ib_tasklet_fn_recv(unsigned long data) { struct rds_ib_connection *ic = (struct rds_ib_connection *)data; @@ -304,6 +327,18 @@ static void rds_ib_qp_event_handler(struct ib_event *event, void *data) } } +static void rds_ib_cq_comp_handler_send(struct ib_cq *cq, void *context) +{ + struct rds_connection *conn = context; + struct rds_ib_connection *ic = conn->c_transport_data; + + rdsdebug("conn %p cq %p\n", conn, cq); + + rds_ib_stats_inc(s_ib_evt_handler_call); + + tasklet_schedule(&ic->i_send_tasklet); +} + /* * This needs to be very careful to not leave IS_ERR pointers around for * cleanup to trip over. @@ -337,7 +372,8 @@ static int rds_ib_setup_qp(struct rds_connection *conn) ic->i_pd = rds_ibdev->pd; cq_attr.cqe = ic->i_send_ring.w_nr + 1; - ic->i_send_cq = ib_create_cq(dev, rds_ib_send_cq_comp_handler, + + ic->i_send_cq = ib_create_cq(dev, rds_ib_cq_comp_handler_send, rds_ib_cq_event_handler, conn, &cq_attr); if (IS_ERR(ic->i_send_cq)) { @@ -703,6 +739,7 @@ void rds_ib_conn_shutdown(struct rds_connection *conn) wait_event(rds_ib_ring_empty_wait, rds_ib_ring_empty(&ic->i_recv_ring) && (atomic_read(&ic->i_signaled_sends) == 0)); + tasklet_kill(&ic->i_send_tasklet); tasklet_kill(&ic->i_recv_tasklet); /* first destroy the ib state that generates callbacks */ @@ -809,8 +846,10 @@ int rds_ib_conn_alloc(struct rds_connection *conn, gfp_t gfp) } INIT_LIST_HEAD(&ic->ib_node); + tasklet_init(&ic->i_send_tasklet, rds_ib_tasklet_fn_send, + (unsigned long)ic); tasklet_init(&ic->i_recv_tasklet, rds_ib_tasklet_fn_recv, - (unsigned long) ic); + (unsigned long)ic); mutex_init(&ic->i_recv_mutex); #ifndef KERNEL_HAS_ATOMIC64 spin_lock_init(&ic->i_ack_lock); diff --git a/net/rds/ib_send.c b/net/rds/ib_send.c index 4e88047..670882c 100644 --- a/net/rds/ib_send.c +++ b/net/rds/ib_send.c @@ -195,7 +195,7 @@ void rds_ib_send_init_ring(struct rds_ib_connection *ic) send->s_op = NULL; - send->s_wr.wr_id = i; + send->s_wr.wr_id = i | RDS_IB_SEND_OP; send->s_wr.sg_list = send->s_sge; send->s_wr.ex.imm_data = 0; @@ -237,81 +237,73 @@ static void rds_ib_sub_signaled(struct rds_ib_connection *ic, int nr) * unallocs the next free entry in the ring it doesn't alter which is * the next to be freed, which is what this is concerned with. */ -void rds_ib_send_cq_comp_handler(struct ib_cq *cq, void *context) +void rds_ib_send_cqe_handler(struct rds_ib_connection *ic, struct ib_wc *wc) { - struct rds_connection *conn = context; - struct rds_ib_connection *ic = conn->c_transport_data; struct rds_message *rm = NULL; - struct ib_wc wc; + struct rds_connection *conn = ic->conn; struct rds_ib_send_work *send; u32 completed; u32 oldest; u32 i = 0; - int ret; int nr_sig = 0; - rdsdebug("cq %p conn %p\n", cq, conn); - rds_ib_stats_inc(s_ib_tx_cq_call); - ret = ib_req_notify_cq(cq, IB_CQ_NEXT_COMP); - if (ret) - rdsdebug("ib_req_notify_cq send failed: %d\n", ret); - - while (ib_poll_cq(cq, 1, &wc) > 0) { - rdsdebug("wc wr_id 0x%llx status %u (%s) byte_len %u imm_data %u\n", - (unsigned long long)wc.wr_id, wc.status, - ib_wc_status_msg(wc.status), wc.byte_len, - be32_to_cpu(wc.ex.imm_data)); - rds_ib_stats_inc(s_ib_tx_cq_event); - - if (wc.wr_id == RDS_IB_ACK_WR_ID) { - if (time_after(jiffies, ic->i_ack_queued + HZ/2)) - rds_ib_stats_inc(s_ib_tx_stalled); - rds_ib_ack_send_complete(ic); - continue; - } - oldest = rds_ib_ring_oldest(&ic->i_send_ring); + rdsdebug("wc wr_id 0x%llx status %u (%s) byte_len %u imm_data %u\n", + (unsigned long long)wc->wr_id, wc->status, + ib_wc_status_msg(wc->status), wc->byte_len, + be32_to_cpu(wc->ex.imm_data)); + rds_ib_stats_inc(s_ib_tx_cq_event); - completed = rds_ib_ring_completed(&ic->i_send_ring, wc.wr_id, oldest); + if (wc->wr_id == RDS_IB_ACK_WR_ID) { + if (time_after(jiffies, ic->i_ack_queued + HZ / 2)) + rds_ib_stats_inc(s_ib_tx_stalled); + rds_ib_ack_send_complete(ic); + return; + } - for (i = 0; i < completed; i++) { - send = &ic->i_sends[oldest]; - if (send->s_wr.send_flags & IB_SEND_SIGNALED) - nr_sig++; + oldest = rds_ib_ring_oldest(&ic->i_send_ring); - rm = rds_ib_send_unmap_op(ic, send, wc.status); + completed = rds_ib_ring_completed(&ic->i_send_ring, + (wc->wr_id & ~RDS_IB_SEND_OP), + oldest); - if (time_after(jiffies, send->s_queued + HZ/2)) - rds_ib_stats_inc(s_ib_tx_stalled); + for (i = 0; i < completed; i++) { + send = &ic->i_sends[oldest]; + if (send->s_wr.send_flags & IB_SEND_SIGNALED) + nr_sig++; - if (send->s_op) { - if (send->s_op == rm->m_final_op) { - /* If anyone waited for this message to get flushed out, wake - * them up now */ - rds_message_unmapped(rm); - } - rds_message_put(rm); - send->s_op = NULL; - } + rm = rds_ib_send_unmap_op(ic, send, wc->status); - oldest = (oldest + 1) % ic->i_send_ring.w_nr; - } + if (time_after(jiffies, send->s_queued + HZ / 2)) + rds_ib_stats_inc(s_ib_tx_stalled); - rds_ib_ring_free(&ic->i_send_ring, completed); - rds_ib_sub_signaled(ic, nr_sig); - nr_sig = 0; - - if (test_and_clear_bit(RDS_LL_SEND_FULL, &conn->c_flags) || - test_bit(0, &conn->c_map_queued)) - queue_delayed_work(rds_wq, &conn->c_send_w, 0); - - /* We expect errors as the qp is drained during shutdown */ - if (wc.status != IB_WC_SUCCESS && rds_conn_up(conn)) { - rds_ib_conn_error(conn, "send completion on %pI4 had status " - "%u (%s), disconnecting and reconnecting\n", - &conn->c_faddr, wc.status, - ib_wc_status_msg(wc.status)); + if (send->s_op) { + if (send->s_op == rm->m_final_op) { + /* If anyone waited for this message to get + * flushed out, wake them up now + */ + rds_message_unmapped(rm); + } + rds_message_put(rm); + send->s_op = NULL; } + + oldest = (oldest + 1) % ic->i_send_ring.w_nr; + } + + rds_ib_ring_free(&ic->i_send_ring, completed); + rds_ib_sub_signaled(ic, nr_sig); + nr_sig = 0; + + if (test_and_clear_bit(RDS_LL_SEND_FULL, &conn->c_flags) || + test_bit(0, &conn->c_map_queued)) + queue_delayed_work(rds_wq, &conn->c_send_w, 0); + + /* We expect errors as the qp is drained during shutdown */ + if (wc->status != IB_WC_SUCCESS && rds_conn_up(conn)) { + rds_ib_conn_error(conn, "send completion on %pI4 had status %u (%s), disconnecting and reconnecting\n", + &conn->c_faddr, wc->status, + ib_wc_status_msg(wc->status)); } } diff --git a/net/rds/ib_stats.c b/net/rds/ib_stats.c index bdf6115..8c8b84f 100644 --- a/net/rds/ib_stats.c +++ b/net/rds/ib_stats.c @@ -43,7 +43,6 @@ static const char *const rds_ib_stat_names[] = { "ib_connect_raced", "ib_listen_closed_stale", "s_ib_evt_handler_call", - "ib_tx_cq_call", "ib_tasklet_call", "ib_tx_cq_event", "ib_tx_ring_full", diff --git a/net/rds/send.c b/net/rds/send.c index a081a64..ee49c25 100644 --- a/net/rds/send.c +++ b/net/rds/send.c @@ -432,6 +432,7 @@ over_batch: out: return ret; } +EXPORT_SYMBOL_GPL(rds_send_xmit); static void rds_send_sndbuf_remove(struct rds_sock *rs, struct rds_message *rm) { -- cgit v0.10.2 From 9441c973e1e0e9885537a3a86020fe8e121e9a98 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Sat, 19 Sep 2015 14:01:09 -0400 Subject: RDS: IB: handle rds_ibdev release case instead of crashing the kernel Just in case we are still handling the QP receive completion while the rds_ibdev is released, drop the connection instead of crashing the kernel. Signed-off-by: Santosh Shilimkar Signed-off-by: Santosh Shilimkar diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c index 8f51d0d..2b2370e 100644 --- a/net/rds/ib_cm.c +++ b/net/rds/ib_cm.c @@ -285,7 +285,8 @@ static void rds_ib_tasklet_fn_recv(unsigned long data) struct rds_ib_device *rds_ibdev = ic->rds_ibdev; struct rds_ib_ack_state state; - BUG_ON(!rds_ibdev); + if (!rds_ibdev) + rds_conn_drop(conn); rds_ib_stats_inc(s_ib_tasklet_call); -- cgit v0.10.2 From 2e1d6b813ac146db1e33ebb9b90441012dde4952 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Sun, 13 Sep 2015 22:34:37 -0700 Subject: RDS: IB: fix the rds_ib_fmr_wq kick call RDS IB mr pool has its own workqueue 'rds_ib_fmr_wq', so we need to use queue_delayed_work() to kick the work. This was hurting the performance since pool maintenance was less often triggered from other path. Signed-off-by: Santosh Shilimkar Signed-off-by: Santosh Shilimkar diff --git a/net/rds/ib_rdma.c b/net/rds/ib_rdma.c index 872f523..b6644fa 100644 --- a/net/rds/ib_rdma.c +++ b/net/rds/ib_rdma.c @@ -319,7 +319,7 @@ static struct rds_ib_mr *rds_ib_alloc_fmr(struct rds_ib_device *rds_ibdev) int err = 0, iter = 0; if (atomic_read(&pool->dirty_count) >= pool->max_items / 10) - schedule_delayed_work(&pool->flush_worker, 10); + queue_delayed_work(rds_ib_fmr_wq, &pool->flush_worker, 10); while (1) { ibmr = rds_ib_reuse_fmr(pool); -- cgit v0.10.2 From 26139dc1dbf79fd1ae1e2766a1f66b0728bd67b3 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Tue, 15 Sep 2015 18:20:35 -0700 Subject: RDS: IB: use already available pool handle from ibmr rds_ib_mr already keeps the pool handle which it associates with. Lets use that instead of round about way of fetching it from rds_ib_device. No functional change. Signed-off-by: Santosh Shilimkar Signed-off-by: Santosh Shilimkar diff --git a/net/rds/ib_rdma.c b/net/rds/ib_rdma.c index b6644fa..52d889a 100644 --- a/net/rds/ib_rdma.c +++ b/net/rds/ib_rdma.c @@ -522,8 +522,7 @@ static void rds_ib_teardown_mr(struct rds_ib_mr *ibmr) __rds_ib_teardown_mr(ibmr); if (pinned) { - struct rds_ib_device *rds_ibdev = ibmr->device; - struct rds_ib_mr_pool *pool = rds_ibdev->mr_pool; + struct rds_ib_mr_pool *pool = ibmr->pool; atomic_sub(pinned, &pool->free_pinned); } @@ -717,8 +716,8 @@ static void rds_ib_mr_pool_flush_worker(struct work_struct *work) void rds_ib_free_mr(void *trans_private, int invalidate) { struct rds_ib_mr *ibmr = trans_private; + struct rds_ib_mr_pool *pool = ibmr->pool; struct rds_ib_device *rds_ibdev = ibmr->device; - struct rds_ib_mr_pool *pool = rds_ibdev->mr_pool; rdsdebug("RDS/IB: free_mr nents %u\n", ibmr->sg_len); -- cgit v0.10.2 From 67161e250a28de5cdafa99a3b659b1e2e269fd7e Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Sat, 19 Sep 2015 17:21:22 -0400 Subject: RDS: IB: mark rds_ib_fmr_wq static Fix below warning by marking rds_ib_fmr_wq static net/rds/ib_rdma.c:87:25: warning: symbol 'rds_ib_fmr_wq' was not declared. Should it be static? Signed-off-by: Santosh Shilimkar Signed-off-by: Santosh Shilimkar diff --git a/net/rds/ib_rdma.c b/net/rds/ib_rdma.c index 52d889a..bb62024 100644 --- a/net/rds/ib_rdma.c +++ b/net/rds/ib_rdma.c @@ -83,7 +83,7 @@ struct rds_ib_mr_pool { struct ib_fmr_attr fmr_attr; }; -struct workqueue_struct *rds_ib_fmr_wq; +static struct workqueue_struct *rds_ib_fmr_wq; int rds_ib_fmr_init(void) { -- cgit v0.10.2 From 41a4e9646229801624e38f7a1cc53033a0affdb1 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Sat, 19 Sep 2015 13:06:08 -0400 Subject: RDS: IB: use max_mr from HCA caps than max_fmr All HCA drivers seems to popullate max_mr caps and few of them do both max_mr and max_fmr. Hence update RDS code to make use of max_mr. Signed-off-by: Santosh Shilimkar Signed-off-by: Santosh Shilimkar diff --git a/net/rds/ib.c b/net/rds/ib.c index 2d3f2ab..883813a 100644 --- a/net/rds/ib.c +++ b/net/rds/ib.c @@ -148,8 +148,8 @@ static void rds_ib_add_one(struct ib_device *device) rds_ibdev->max_sge = min(dev_attr->max_sge, RDS_IB_MAX_SGE); rds_ibdev->fmr_max_remaps = dev_attr->max_map_per_fmr?: 32; - rds_ibdev->max_fmrs = dev_attr->max_fmr ? - min_t(unsigned int, dev_attr->max_fmr, fmr_pool_size) : + rds_ibdev->max_fmrs = dev_attr->max_mr ? + min_t(unsigned int, dev_attr->max_mr, fmr_pool_size) : fmr_pool_size; rds_ibdev->max_initiator_depth = dev_attr->max_qp_init_rd_atom; -- cgit v0.10.2 From 06766513232d1619ac84e87b1d839d3fcc23a540 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Thu, 10 Sep 2015 21:20:57 -0700 Subject: RDS: IB: split mr pool to improve 8K messages performance 8K message sizes are pretty important usecase for RDS current workloads so we make provison to have 8K mrs available from the pool. Based on number of SG's in the RDS message, we pick a pool to use. Also to make sure that we don't under utlise mrs when say 8k messages are dominating which could lead to 8k pull being exhausted, we fall-back to 1m pool till 8k pool recovers for use. This helps to at least push ~55 kB/s bidirectional data which is a nice improvement. Signed-off-by: Santosh Shilimkar Signed-off-by: Santosh Shilimkar diff --git a/net/rds/ib.c b/net/rds/ib.c index 883813a..a833ab7 100644 --- a/net/rds/ib.c +++ b/net/rds/ib.c @@ -43,14 +43,14 @@ #include "rds.h" #include "ib.h" -static unsigned int fmr_pool_size = RDS_FMR_POOL_SIZE; -unsigned int fmr_message_size = RDS_FMR_SIZE + 1; /* +1 allows for unaligned MRs */ +unsigned int rds_ib_fmr_1m_pool_size = RDS_FMR_1M_POOL_SIZE; +unsigned int rds_ib_fmr_8k_pool_size = RDS_FMR_8K_POOL_SIZE; unsigned int rds_ib_retry_count = RDS_IB_DEFAULT_RETRY_COUNT; -module_param(fmr_pool_size, int, 0444); -MODULE_PARM_DESC(fmr_pool_size, " Max number of fmr per HCA"); -module_param(fmr_message_size, int, 0444); -MODULE_PARM_DESC(fmr_message_size, " Max size of a RDMA transfer"); +module_param(rds_ib_fmr_1m_pool_size, int, 0444); +MODULE_PARM_DESC(rds_ib_fmr_1m_pool_size, " Max number of 1M fmr per HCA"); +module_param(rds_ib_fmr_8k_pool_size, int, 0444); +MODULE_PARM_DESC(rds_ib_fmr_8k_pool_size, " Max number of 8K fmr per HCA"); module_param(rds_ib_retry_count, int, 0444); MODULE_PARM_DESC(rds_ib_retry_count, " Number of hw retries before reporting an error"); @@ -97,8 +97,10 @@ static void rds_ib_dev_free(struct work_struct *work) struct rds_ib_device *rds_ibdev = container_of(work, struct rds_ib_device, free_work); - if (rds_ibdev->mr_pool) - rds_ib_destroy_mr_pool(rds_ibdev->mr_pool); + if (rds_ibdev->mr_8k_pool) + rds_ib_destroy_mr_pool(rds_ibdev->mr_8k_pool); + if (rds_ibdev->mr_1m_pool) + rds_ib_destroy_mr_pool(rds_ibdev->mr_1m_pool); if (rds_ibdev->pd) ib_dealloc_pd(rds_ibdev->pd); @@ -148,9 +150,13 @@ static void rds_ib_add_one(struct ib_device *device) rds_ibdev->max_sge = min(dev_attr->max_sge, RDS_IB_MAX_SGE); rds_ibdev->fmr_max_remaps = dev_attr->max_map_per_fmr?: 32; - rds_ibdev->max_fmrs = dev_attr->max_mr ? - min_t(unsigned int, dev_attr->max_mr, fmr_pool_size) : - fmr_pool_size; + rds_ibdev->max_1m_fmrs = dev_attr->max_mr ? + min_t(unsigned int, (dev_attr->max_mr / 2), + rds_ib_fmr_1m_pool_size) : rds_ib_fmr_1m_pool_size; + + rds_ibdev->max_8k_fmrs = dev_attr->max_mr ? + min_t(unsigned int, ((dev_attr->max_mr / 2) * RDS_MR_8K_SCALE), + rds_ib_fmr_8k_pool_size) : rds_ib_fmr_8k_pool_size; rds_ibdev->max_initiator_depth = dev_attr->max_qp_init_rd_atom; rds_ibdev->max_responder_resources = dev_attr->max_qp_rd_atom; @@ -162,12 +168,25 @@ static void rds_ib_add_one(struct ib_device *device) goto put_dev; } - rds_ibdev->mr_pool = rds_ib_create_mr_pool(rds_ibdev); - if (IS_ERR(rds_ibdev->mr_pool)) { - rds_ibdev->mr_pool = NULL; + rds_ibdev->mr_1m_pool = + rds_ib_create_mr_pool(rds_ibdev, RDS_IB_MR_1M_POOL); + if (IS_ERR(rds_ibdev->mr_1m_pool)) { + rds_ibdev->mr_1m_pool = NULL; goto put_dev; } + rds_ibdev->mr_8k_pool = + rds_ib_create_mr_pool(rds_ibdev, RDS_IB_MR_8K_POOL); + if (IS_ERR(rds_ibdev->mr_8k_pool)) { + rds_ibdev->mr_8k_pool = NULL; + goto put_dev; + } + + rdsdebug("RDS/IB: max_mr = %d, max_wrs = %d, max_sge = %d, fmr_max_remaps = %d, max_1m_fmrs = %d, max_8k_fmrs = %d\n", + dev_attr->max_fmr, rds_ibdev->max_wrs, rds_ibdev->max_sge, + rds_ibdev->fmr_max_remaps, rds_ibdev->max_1m_fmrs, + rds_ibdev->max_8k_fmrs); + INIT_LIST_HEAD(&rds_ibdev->ipaddr_list); INIT_LIST_HEAD(&rds_ibdev->conn_list); diff --git a/net/rds/ib.h b/net/rds/ib.h index 3a8cd31..f17d095 100644 --- a/net/rds/ib.h +++ b/net/rds/ib.h @@ -9,8 +9,11 @@ #include "rds.h" #include "rdma_transport.h" -#define RDS_FMR_SIZE 256 -#define RDS_FMR_POOL_SIZE 8192 +#define RDS_FMR_1M_POOL_SIZE (8192 / 2) +#define RDS_FMR_1M_MSG_SIZE 256 +#define RDS_FMR_8K_MSG_SIZE 2 +#define RDS_MR_8K_SCALE (256 / (RDS_FMR_8K_MSG_SIZE + 1)) +#define RDS_FMR_8K_POOL_SIZE (RDS_MR_8K_SCALE * (8192 / 2)) #define RDS_IB_MAX_SGE 8 #define RDS_IB_RECV_SGE 2 @@ -189,15 +192,23 @@ struct rds_ib_ipaddr { struct rcu_head rcu; }; +enum { + RDS_IB_MR_8K_POOL, + RDS_IB_MR_1M_POOL, +}; + struct rds_ib_device { struct list_head list; struct list_head ipaddr_list; struct list_head conn_list; struct ib_device *dev; struct ib_pd *pd; - struct rds_ib_mr_pool *mr_pool; - unsigned int fmr_max_remaps; unsigned int max_fmrs; + struct rds_ib_mr_pool *mr_1m_pool; + struct rds_ib_mr_pool *mr_8k_pool; + unsigned int fmr_max_remaps; + unsigned int max_8k_fmrs; + unsigned int max_1m_fmrs; int max_sge; unsigned int max_wrs; unsigned int max_initiator_depth; @@ -239,12 +250,18 @@ struct rds_ib_statistics { uint64_t s_ib_ack_send_delayed; uint64_t s_ib_ack_send_piggybacked; uint64_t s_ib_ack_received; - uint64_t s_ib_rdma_mr_alloc; - uint64_t s_ib_rdma_mr_free; - uint64_t s_ib_rdma_mr_used; - uint64_t s_ib_rdma_mr_pool_flush; - uint64_t s_ib_rdma_mr_pool_wait; - uint64_t s_ib_rdma_mr_pool_depleted; + uint64_t s_ib_rdma_mr_8k_alloc; + uint64_t s_ib_rdma_mr_8k_free; + uint64_t s_ib_rdma_mr_8k_used; + uint64_t s_ib_rdma_mr_8k_pool_flush; + uint64_t s_ib_rdma_mr_8k_pool_wait; + uint64_t s_ib_rdma_mr_8k_pool_depleted; + uint64_t s_ib_rdma_mr_1m_alloc; + uint64_t s_ib_rdma_mr_1m_free; + uint64_t s_ib_rdma_mr_1m_used; + uint64_t s_ib_rdma_mr_1m_pool_flush; + uint64_t s_ib_rdma_mr_1m_pool_wait; + uint64_t s_ib_rdma_mr_1m_pool_depleted; uint64_t s_ib_atomic_cswp; uint64_t s_ib_atomic_fadd; }; @@ -296,7 +313,8 @@ struct rds_ib_device *rds_ib_get_client_data(struct ib_device *device); void rds_ib_dev_put(struct rds_ib_device *rds_ibdev); extern struct ib_client rds_ib_client; -extern unsigned int fmr_message_size; +extern unsigned int rds_ib_fmr_1m_pool_size; +extern unsigned int rds_ib_fmr_8k_pool_size; extern unsigned int rds_ib_retry_count; extern spinlock_t ib_nodev_conns_lock; @@ -326,7 +344,8 @@ int rds_ib_update_ipaddr(struct rds_ib_device *rds_ibdev, __be32 ipaddr); void rds_ib_add_conn(struct rds_ib_device *rds_ibdev, struct rds_connection *conn); void rds_ib_remove_conn(struct rds_ib_device *rds_ibdev, struct rds_connection *conn); void rds_ib_destroy_nodev_conns(void); -struct rds_ib_mr_pool *rds_ib_create_mr_pool(struct rds_ib_device *); +struct rds_ib_mr_pool *rds_ib_create_mr_pool(struct rds_ib_device *rds_dev, + int npages); void rds_ib_get_mr_info(struct rds_ib_device *rds_ibdev, struct rds_info_rdma_connection *iinfo); void rds_ib_destroy_mr_pool(struct rds_ib_mr_pool *); void *rds_ib_get_mr(struct scatterlist *sg, unsigned long nents, diff --git a/net/rds/ib_rdma.c b/net/rds/ib_rdma.c index bb62024..a234074 100644 --- a/net/rds/ib_rdma.c +++ b/net/rds/ib_rdma.c @@ -65,6 +65,7 @@ struct rds_ib_mr { * Our own little FMR pool */ struct rds_ib_mr_pool { + unsigned int pool_type; struct mutex flush_lock; /* serialize fmr invalidate */ struct delayed_work flush_worker; /* flush worker */ @@ -234,7 +235,8 @@ void rds_ib_destroy_nodev_conns(void) rds_conn_destroy(ic->conn); } -struct rds_ib_mr_pool *rds_ib_create_mr_pool(struct rds_ib_device *rds_ibdev) +struct rds_ib_mr_pool *rds_ib_create_mr_pool(struct rds_ib_device *rds_ibdev, + int pool_type) { struct rds_ib_mr_pool *pool; @@ -242,6 +244,7 @@ struct rds_ib_mr_pool *rds_ib_create_mr_pool(struct rds_ib_device *rds_ibdev) if (!pool) return ERR_PTR(-ENOMEM); + pool->pool_type = pool_type; init_llist_head(&pool->free_list); init_llist_head(&pool->drop_list); init_llist_head(&pool->clean_list); @@ -249,28 +252,30 @@ struct rds_ib_mr_pool *rds_ib_create_mr_pool(struct rds_ib_device *rds_ibdev) init_waitqueue_head(&pool->flush_wait); INIT_DELAYED_WORK(&pool->flush_worker, rds_ib_mr_pool_flush_worker); - pool->fmr_attr.max_pages = fmr_message_size; + if (pool_type == RDS_IB_MR_1M_POOL) { + /* +1 allows for unaligned MRs */ + pool->fmr_attr.max_pages = RDS_FMR_1M_MSG_SIZE + 1; + pool->max_items = RDS_FMR_1M_POOL_SIZE; + } else { + /* pool_type == RDS_IB_MR_8K_POOL */ + pool->fmr_attr.max_pages = RDS_FMR_8K_MSG_SIZE + 1; + pool->max_items = RDS_FMR_8K_POOL_SIZE; + } + + pool->max_free_pinned = pool->max_items * pool->fmr_attr.max_pages / 4; pool->fmr_attr.max_maps = rds_ibdev->fmr_max_remaps; pool->fmr_attr.page_shift = PAGE_SHIFT; - pool->max_free_pinned = rds_ibdev->max_fmrs * fmr_message_size / 4; - - /* We never allow more than max_items MRs to be allocated. - * When we exceed more than max_items_soft, we start freeing - * items more aggressively. - * Make sure that max_items > max_items_soft > max_items / 2 - */ pool->max_items_soft = rds_ibdev->max_fmrs * 3 / 4; - pool->max_items = rds_ibdev->max_fmrs; return pool; } void rds_ib_get_mr_info(struct rds_ib_device *rds_ibdev, struct rds_info_rdma_connection *iinfo) { - struct rds_ib_mr_pool *pool = rds_ibdev->mr_pool; + struct rds_ib_mr_pool *pool_1m = rds_ibdev->mr_1m_pool; - iinfo->rdma_mr_max = pool->max_items; - iinfo->rdma_mr_size = pool->fmr_attr.max_pages; + iinfo->rdma_mr_max = pool_1m->max_items; + iinfo->rdma_mr_size = pool_1m->fmr_attr.max_pages; } void rds_ib_destroy_mr_pool(struct rds_ib_mr_pool *pool) @@ -312,15 +317,29 @@ static inline void wait_clean_list_grace(void) } } -static struct rds_ib_mr *rds_ib_alloc_fmr(struct rds_ib_device *rds_ibdev) +static struct rds_ib_mr *rds_ib_alloc_fmr(struct rds_ib_device *rds_ibdev, + int npages) { - struct rds_ib_mr_pool *pool = rds_ibdev->mr_pool; + struct rds_ib_mr_pool *pool; struct rds_ib_mr *ibmr = NULL; int err = 0, iter = 0; + if (npages <= RDS_FMR_8K_MSG_SIZE) + pool = rds_ibdev->mr_8k_pool; + else + pool = rds_ibdev->mr_1m_pool; + if (atomic_read(&pool->dirty_count) >= pool->max_items / 10) queue_delayed_work(rds_ib_fmr_wq, &pool->flush_worker, 10); + /* Switch pools if one of the pool is reaching upper limit */ + if (atomic_read(&pool->dirty_count) >= pool->max_items * 9 / 10) { + if (pool->pool_type == RDS_IB_MR_8K_POOL) + pool = rds_ibdev->mr_1m_pool; + else + pool = rds_ibdev->mr_8k_pool; + } + while (1) { ibmr = rds_ib_reuse_fmr(pool); if (ibmr) @@ -341,12 +360,18 @@ static struct rds_ib_mr *rds_ib_alloc_fmr(struct rds_ib_device *rds_ibdev) atomic_dec(&pool->item_count); if (++iter > 2) { - rds_ib_stats_inc(s_ib_rdma_mr_pool_depleted); + if (pool->pool_type == RDS_IB_MR_8K_POOL) + rds_ib_stats_inc(s_ib_rdma_mr_8k_pool_depleted); + else + rds_ib_stats_inc(s_ib_rdma_mr_1m_pool_depleted); return ERR_PTR(-EAGAIN); } /* We do have some empty MRs. Flush them out. */ - rds_ib_stats_inc(s_ib_rdma_mr_pool_wait); + if (pool->pool_type == RDS_IB_MR_8K_POOL) + rds_ib_stats_inc(s_ib_rdma_mr_8k_pool_wait); + else + rds_ib_stats_inc(s_ib_rdma_mr_1m_pool_wait); rds_ib_flush_mr_pool(pool, 0, &ibmr); if (ibmr) return ibmr; @@ -371,7 +396,12 @@ static struct rds_ib_mr *rds_ib_alloc_fmr(struct rds_ib_device *rds_ibdev) goto out_no_cigar; } - rds_ib_stats_inc(s_ib_rdma_mr_alloc); + ibmr->pool = pool; + if (pool->pool_type == RDS_IB_MR_8K_POOL) + rds_ib_stats_inc(s_ib_rdma_mr_8k_alloc); + else + rds_ib_stats_inc(s_ib_rdma_mr_1m_alloc); + return ibmr; out_no_cigar: @@ -427,7 +457,7 @@ static int rds_ib_map_fmr(struct rds_ib_device *rds_ibdev, struct rds_ib_mr *ibm } page_cnt += len >> PAGE_SHIFT; - if (page_cnt > fmr_message_size) + if (page_cnt > ibmr->pool->fmr_attr.max_pages) return -EINVAL; dma_pages = kmalloc_node(sizeof(u64) * page_cnt, GFP_ATOMIC, @@ -459,7 +489,10 @@ static int rds_ib_map_fmr(struct rds_ib_device *rds_ibdev, struct rds_ib_mr *ibm ibmr->sg_dma_len = sg_dma_len; ibmr->remap_count++; - rds_ib_stats_inc(s_ib_rdma_mr_used); + if (ibmr->pool->pool_type == RDS_IB_MR_8K_POOL) + rds_ib_stats_inc(s_ib_rdma_mr_8k_used); + else + rds_ib_stats_inc(s_ib_rdma_mr_1m_used); ret = 0; out: @@ -591,7 +624,7 @@ static void list_to_llist_nodes(struct rds_ib_mr_pool *pool, * to free as many MRs as needed to get back to this limit. */ static int rds_ib_flush_mr_pool(struct rds_ib_mr_pool *pool, - int free_all, struct rds_ib_mr **ibmr_ret) + int free_all, struct rds_ib_mr **ibmr_ret) { struct rds_ib_mr *ibmr, *next; struct llist_node *clean_nodes; @@ -602,11 +635,14 @@ static int rds_ib_flush_mr_pool(struct rds_ib_mr_pool *pool, unsigned int nfreed = 0, dirty_to_clean = 0, free_goal; int ret = 0; - rds_ib_stats_inc(s_ib_rdma_mr_pool_flush); + if (pool->pool_type == RDS_IB_MR_8K_POOL) + rds_ib_stats_inc(s_ib_rdma_mr_8k_pool_flush); + else + rds_ib_stats_inc(s_ib_rdma_mr_1m_pool_flush); if (ibmr_ret) { DEFINE_WAIT(wait); - while(!mutex_trylock(&pool->flush_lock)) { + while (!mutex_trylock(&pool->flush_lock)) { ibmr = rds_ib_reuse_fmr(pool); if (ibmr) { *ibmr_ret = ibmr; @@ -663,8 +699,12 @@ static int rds_ib_flush_mr_pool(struct rds_ib_mr_pool *pool, list_for_each_entry_safe(ibmr, next, &unmap_list, unmap_list) { unpinned += ibmr->sg_len; __rds_ib_teardown_mr(ibmr); - if (nfreed < free_goal || ibmr->remap_count >= pool->fmr_attr.max_maps) { - rds_ib_stats_inc(s_ib_rdma_mr_free); + if (nfreed < free_goal || + ibmr->remap_count >= pool->fmr_attr.max_maps) { + if (ibmr->pool->pool_type == RDS_IB_MR_8K_POOL) + rds_ib_stats_inc(s_ib_rdma_mr_8k_free); + else + rds_ib_stats_inc(s_ib_rdma_mr_1m_free); list_del(&ibmr->unmap_list); ib_dealloc_fmr(ibmr->fmr); kfree(ibmr); @@ -756,10 +796,11 @@ void rds_ib_flush_mrs(void) down_read(&rds_ib_devices_lock); list_for_each_entry(rds_ibdev, &rds_ib_devices, list) { - struct rds_ib_mr_pool *pool = rds_ibdev->mr_pool; + if (rds_ibdev->mr_8k_pool) + rds_ib_flush_mr_pool(rds_ibdev->mr_8k_pool, 0, NULL); - if (pool) - rds_ib_flush_mr_pool(pool, 0, NULL); + if (rds_ibdev->mr_1m_pool) + rds_ib_flush_mr_pool(rds_ibdev->mr_1m_pool, 0, NULL); } up_read(&rds_ib_devices_lock); } @@ -777,12 +818,12 @@ void *rds_ib_get_mr(struct scatterlist *sg, unsigned long nents, goto out; } - if (!rds_ibdev->mr_pool) { + if (!rds_ibdev->mr_8k_pool || !rds_ibdev->mr_1m_pool) { ret = -ENODEV; goto out; } - ibmr = rds_ib_alloc_fmr(rds_ibdev); + ibmr = rds_ib_alloc_fmr(rds_ibdev, nents); if (IS_ERR(ibmr)) { rds_ib_dev_put(rds_ibdev); return ibmr; diff --git a/net/rds/ib_stats.c b/net/rds/ib_stats.c index 8c8b84f..d77e044 100644 --- a/net/rds/ib_stats.c +++ b/net/rds/ib_stats.c @@ -61,12 +61,18 @@ static const char *const rds_ib_stat_names[] = { "ib_ack_send_delayed", "ib_ack_send_piggybacked", "ib_ack_received", - "ib_rdma_mr_alloc", - "ib_rdma_mr_free", - "ib_rdma_mr_used", - "ib_rdma_mr_pool_flush", - "ib_rdma_mr_pool_wait", - "ib_rdma_mr_pool_depleted", + "ib_rdma_mr_8k_alloc", + "ib_rdma_mr_8k_free", + "ib_rdma_mr_8k_used", + "ib_rdma_mr_8k_pool_flush", + "ib_rdma_mr_8k_pool_wait", + "ib_rdma_mr_8k_pool_depleted", + "ib_rdma_mr_1m_alloc", + "ib_rdma_mr_1m_free", + "ib_rdma_mr_1m_used", + "ib_rdma_mr_1m_pool_flush", + "ib_rdma_mr_1m_pool_wait", + "ib_rdma_mr_1m_pool_depleted", "ib_atomic_cswp", "ib_atomic_fadd", }; -- cgit v0.10.2 From 0a837fe4724713ef701e47d6bfab98a5efaff3eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20N=C3=B8rlund?= Date: Tue, 6 Oct 2015 07:24:47 +0200 Subject: ipv4: Fix compilation errors in fib_rebalance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes net/built-in.o: In function `fib_rebalance': fib_semantics.c:(.text+0x9df14): undefined reference to `__divdi3' and net/built-in.o: In function `fib_rebalance': net/ipv4/fib_semantics.c:572: undefined reference to `__aeabi_ldivmod' Fixes: 0e884c78ee19 ("ipv4: L3 hash-based multipath") Signed-off-by: Peter Nørlund Signed-off-by: David S. Miller diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 0c49d2f..7bd698c 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -569,8 +569,8 @@ static void fib_rebalance(struct fib_info *fi) upper_bound = -1; } else { w += nexthop_nh->nh_weight; - upper_bound = DIV_ROUND_CLOSEST(2147483648LL * w, - total) - 1; + upper_bound = DIV_ROUND_CLOSEST_ULL((u64)w << 31, + total) - 1; } atomic_set(&nexthop_nh->nh_upper_bound, upper_bound); -- cgit v0.10.2 From c664bc6d946d253077f3a5d5ca6769492de59e04 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 6 Oct 2015 06:25:29 -0700 Subject: Revert "net: encx24j600_exit() can be static" This reverts commit 9886ce2b9d4e5a8bb3d78d0f7eff3c0f1ed58d67. diff --git a/drivers/net/ethernet/microchip/encx24j600.c b/drivers/net/ethernet/microchip/encx24j600.c index afc008a..1da37fd 100644 --- a/drivers/net/ethernet/microchip/encx24j600.c +++ b/drivers/net/ethernet/microchip/encx24j600.c @@ -1112,7 +1112,7 @@ static int __init encx24j600_init(void) } module_init(encx24j600_init); -static void encx24j600_exit(void) +void encx24j600_exit(void) { spi_unregister_driver(&encx24j600_spi_net_driver); } -- cgit v0.10.2 From 6a27a6c3be95d382cd158c5705c1840be291f28f Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 6 Oct 2015 06:25:36 -0700 Subject: Revert "net: Microchip encx24j600 driver" This reverts commit 04fbfce7a222327b97ca165294ef19f0faa45960. diff --git a/drivers/net/ethernet/microchip/Kconfig b/drivers/net/ethernet/microchip/Kconfig index 992b014..3fd8ca6 100644 --- a/drivers/net/ethernet/microchip/Kconfig +++ b/drivers/net/ethernet/microchip/Kconfig @@ -33,13 +33,4 @@ config ENC28J60_WRITEVERIFY Enable the verify after the buffer write useful for debugging purpose. If unsure, say N. -config ENCX24J600 - tristate "ENCX24J600 support" - depends on SPI - ---help--- - Support for the Microchip ENC424J600 ethernet chip. - - To compile this driver as a module, choose M here. The module will be - called enc424j600. - endif # NET_VENDOR_MICROCHIP diff --git a/drivers/net/ethernet/microchip/Makefile b/drivers/net/ethernet/microchip/Makefile index ff78f62..573d429 100644 --- a/drivers/net/ethernet/microchip/Makefile +++ b/drivers/net/ethernet/microchip/Makefile @@ -3,4 +3,3 @@ # obj-$(CONFIG_ENC28J60) += enc28j60.o -obj-$(CONFIG_ENCX24J600) += encx24j600.o encx24j600-regmap.o diff --git a/drivers/net/ethernet/microchip/encx24j600-regmap.c b/drivers/net/ethernet/microchip/encx24j600-regmap.c deleted file mode 100644 index f1d74e3..0000000 --- a/drivers/net/ethernet/microchip/encx24j600-regmap.c +++ /dev/null @@ -1,551 +0,0 @@ -/** - * Register map access API - ENCX24J600 support - * - * Copyright 2015 Gridpoint - * - * Author: Jon Ringle - * - * 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 "encx24j600_hw.h" - -static inline bool is_bits_set(int value, int mask) -{ - return (value & mask) == mask; -} - -static int encx24j600_switch_bank(struct encx24j600_context *ctx, - int bank) -{ - int ret = 0; - - int bank_opcode = BANK_SELECT(bank); - ret = spi_write(ctx->spi, &bank_opcode, 1); - if (ret == 0) - ctx->bank = bank; - - return ret; -} - -static int encx24j600_cmdn(struct encx24j600_context *ctx, u8 opcode, - const void *buf, size_t len) -{ - struct spi_message m; - struct spi_transfer t[2] = { { .tx_buf = &opcode, .len = 1, }, - { .tx_buf = buf, .len = len }, }; - spi_message_init(&m); - spi_message_add_tail(&t[0], &m); - spi_message_add_tail(&t[1], &m); - - return spi_sync(ctx->spi, &m); -} - -static void regmap_lock_mutex(void *context) -{ - struct encx24j600_context *ctx = context; - mutex_lock(&ctx->mutex); -} - -static void regmap_unlock_mutex(void *context) -{ - struct encx24j600_context *ctx = context; - mutex_unlock(&ctx->mutex); -} - -static int regmap_encx24j600_sfr_read(void *context, u8 reg, u8 *val, - size_t len) -{ - struct encx24j600_context *ctx = context; - u8 banked_reg = reg & ADDR_MASK; - u8 bank = ((reg & BANK_MASK) >> BANK_SHIFT); - u8 cmd = RCRU; - int ret = 0; - int i = 0; - u8 tx_buf[2]; - - if (reg < 0x80) { - cmd = RCRCODE | banked_reg; - if ((banked_reg < 0x16) && (ctx->bank != bank)) - ret = encx24j600_switch_bank(ctx, bank); - if (unlikely(ret)) - return ret; - } else { - /* Translate registers that are more effecient using - * 3-byte SPI commands - */ - switch (reg) { - case EGPRDPT: - cmd = RGPRDPT; break; - case EGPWRPT: - cmd = RGPWRPT; break; - case ERXRDPT: - cmd = RRXRDPT; break; - case ERXWRPT: - cmd = RRXWRPT; break; - case EUDARDPT: - cmd = RUDARDPT; break; - case EUDAWRPT: - cmd = RUDAWRPT; break; - case EGPDATA: - case ERXDATA: - case EUDADATA: - default: - return -EINVAL; - } - } - - tx_buf[i++] = cmd; - if (cmd == RCRU) - tx_buf[i++] = reg; - - ret = spi_write_then_read(ctx->spi, tx_buf, i, val, len); - - return ret; -} - -static int regmap_encx24j600_sfr_update(struct encx24j600_context *ctx, - u8 reg, u8 *val, size_t len, - u8 unbanked_cmd, u8 banked_code) -{ - u8 banked_reg = reg & ADDR_MASK; - u8 bank = ((reg & BANK_MASK) >> BANK_SHIFT); - u8 cmd = unbanked_cmd; - struct spi_message m; - struct spi_transfer t[3] = { { .tx_buf = &cmd, .len = sizeof(cmd), }, - { .tx_buf = ®, .len = sizeof(reg), }, - { .tx_buf = val, .len = len }, }; - - if (reg < 0x80) { - int ret = 0; - cmd = banked_code | banked_reg; - if ((banked_reg < 0x16) && (ctx->bank != bank)) - ret = encx24j600_switch_bank(ctx, bank); - if (unlikely(ret)) - return ret; - } else { - /* Translate registers that are more effecient using - * 3-byte SPI commands - */ - switch (reg) { - case EGPRDPT: - cmd = WGPRDPT; break; - case EGPWRPT: - cmd = WGPWRPT; break; - case ERXRDPT: - cmd = WRXRDPT; break; - case ERXWRPT: - cmd = WRXWRPT; break; - case EUDARDPT: - cmd = WUDARDPT; break; - case EUDAWRPT: - cmd = WUDAWRPT; break; - case EGPDATA: - case ERXDATA: - case EUDADATA: - default: - return -EINVAL; - } - } - - spi_message_init(&m); - spi_message_add_tail(&t[0], &m); - - if (cmd == unbanked_cmd) { - t[1].tx_buf = ® - spi_message_add_tail(&t[1], &m); - } - - spi_message_add_tail(&t[2], &m); - return spi_sync(ctx->spi, &m); -} - -static int regmap_encx24j600_sfr_write(void *context, u8 reg, u8 *val, - size_t len) -{ - struct encx24j600_context *ctx = context; - return regmap_encx24j600_sfr_update(ctx, reg, val, len, WCRU, WCRCODE); -} - -static int regmap_encx24j600_sfr_set_bits(struct encx24j600_context *ctx, - u8 reg, u8 val) -{ - return regmap_encx24j600_sfr_update(ctx, reg, &val, 1, BFSU, BFSCODE); -} - -static int regmap_encx24j600_sfr_clr_bits(struct encx24j600_context *ctx, - u8 reg, u8 val) -{ - return regmap_encx24j600_sfr_update(ctx, reg, &val, 1, BFCU, BFCCODE); -} - -static int regmap_encx24j600_reg_update_bits(void *context, unsigned int reg, - unsigned int mask, - unsigned int val, bool *change, - bool force_write) -{ - struct encx24j600_context *ctx = context; - - int ret = 0; - unsigned int set_mask = mask & val; - unsigned int clr_mask = mask & ~val; - - if (change) - *change = false; - - if ((reg >= 0x40 && reg < 0x6c) || reg >= 0x80) { - /* Must do read/modify/write cycles for - * MAC/MII regs or Unbanked SFR regs - */ - u16 tmp, orig; - - ret = regmap_encx24j600_sfr_read(context, reg, (u8 *)&orig, - sizeof(orig)); - if (ret != 0) - return ret; - - tmp = orig & ~mask; - tmp |= val & mask; - - if (force_write || (tmp != orig)) { - ret = regmap_encx24j600_sfr_write(context, reg, - (u8 *)&tmp, - sizeof(tmp)); - if (change) - *change = true; - } else if (change) { - *change = false; - } - - return ret; - } - - if (set_mask & 0xff) { - ret = regmap_encx24j600_sfr_set_bits(ctx, reg, set_mask); - if (ret == 0 && change) - *change = true; - } - set_mask = (set_mask & 0xff00) >> 8; - - if ((set_mask & 0xff) && (ret == 0)) { - ret = regmap_encx24j600_sfr_set_bits(ctx, reg + 1, set_mask); - if (ret == 0 && change) - *change = true; - } - - if ((clr_mask & 0xff) && (ret == 0)) { - ret = regmap_encx24j600_sfr_clr_bits(ctx, reg, clr_mask); - if (ret == 0 && change) - *change = true; - } - clr_mask = (clr_mask & 0xff00) >> 8; - - if ((clr_mask & 0xff) && (ret == 0)) { - ret = regmap_encx24j600_sfr_clr_bits(ctx, reg + 1, clr_mask); - if (ret == 0 && change) - *change = true; - } - - return ret; -} - -int regmap_encx24j600_spi_write(void *context, u8 reg, const u8 *data, - size_t count) -{ - struct encx24j600_context *ctx = context; - - if (reg < 0xc0) - return encx24j600_cmdn(ctx, reg, data, count); - else - /* SPI 1-byte command. Ignore data */ - return spi_write(ctx->spi, ®, 1); -} -EXPORT_SYMBOL_GPL(regmap_encx24j600_spi_write); - -int regmap_encx24j600_spi_read(void *context, u8 reg, u8 *data, size_t count) -{ - struct encx24j600_context *ctx = context; - - if (reg == RBSEL && count > 1) - count = 1; - - return spi_write_then_read(ctx->spi, ®, sizeof(reg), data, count); -} -EXPORT_SYMBOL_GPL(regmap_encx24j600_spi_read); - -static int regmap_encx24j600_write(void *context, const void *data, - size_t len) -{ - u8 *dout = (u8 *)data; - u8 reg = dout[0]; - ++dout; - --len; - - if (reg > 0xa0) - return regmap_encx24j600_spi_write(context, reg, dout, len); - - if (len > 2) - return -EINVAL; - - return regmap_encx24j600_sfr_write(context, reg, dout, len); -} - -static int regmap_encx24j600_read(void *context, - const void *reg_buf, size_t reg_size, - void *val, size_t val_size) -{ - u8 reg = *(const u8 *)reg_buf; - - if (reg_size != 1) { - pr_err("%s: reg=%02x reg_size=%zu\n", __func__, reg, reg_size); - return -EINVAL; - } - - if (reg > 0xa0) - return regmap_encx24j600_spi_read(context, reg, val, val_size); - - if (val_size > 2) { - pr_err("%s: reg=%02x val_size=%zu\n", __func__, reg, val_size); - return -EINVAL; - } - - return regmap_encx24j600_sfr_read(context, reg, val, val_size); -} - -static bool encx24j600_regmap_readable(struct device *dev, unsigned int reg) -{ - if ((reg < 0x36) || - ((reg >= 0x40) && (reg < 0x4c)) || - ((reg >= 0x52) && (reg < 0x56)) || - ((reg >= 0x60) && (reg < 0x66)) || - ((reg >= 0x68) && (reg < 0x80)) || - ((reg >= 0x86) && (reg < 0x92)) || - (reg == 0xc8)) - return true; - else - return false; -} - -static bool encx24j600_regmap_writeable(struct device *dev, unsigned int reg) -{ - if ((reg < 0x12) || - ((reg >= 0x14) && (reg < 0x1a)) || - ((reg >= 0x1c) && (reg < 0x36)) || - ((reg >= 0x40) && (reg < 0x4c)) || - ((reg >= 0x52) && (reg < 0x56)) || - ((reg >= 0x60) && (reg < 0x68)) || - ((reg >= 0x6c) && (reg < 0x80)) || - ((reg >= 0x86) && (reg < 0x92)) || - ((reg >= 0xc0) && (reg < 0xc8)) || - ((reg >= 0xca) && (reg < 0xf0))) - return true; - else - return false; -} - -static bool encx24j600_regmap_volatile(struct device *dev, unsigned int reg) -{ - switch (reg) { - case ERXHEAD: - case EDMACS: - case ETXSTAT: - case ETXWIRE: - case ECON1: /* Can be modified via single byte cmds */ - case ECON2: /* Can be modified via single byte cmds */ - case ESTAT: - case EIR: /* Can be modified via single byte cmds */ - case MIRD: - case MISTAT: - return true; - default: - break; - } - - return false; -} - -static bool encx24j600_regmap_precious(struct device *dev, unsigned int reg) -{ - /* single byte cmds are precious */ - if (((reg >= 0xc0) && (reg < 0xc8)) || - ((reg >= 0xca) && (reg < 0xf0))) - return true; - else - return false; -} - -static int regmap_encx24j600_phy_reg_read(void *context, unsigned int reg, - unsigned int *val) -{ - struct encx24j600_context *ctx = context; - int ret; - unsigned int mistat; - - reg = MIREGADR_VAL | (reg & PHREG_MASK); - ret = regmap_write(ctx->regmap, MIREGADR, reg); - if (unlikely(ret)) - goto err_out; - - ret = regmap_write(ctx->regmap, MICMD, MIIRD); - if (unlikely(ret)) - goto err_out; - - usleep_range(26, 100); - while ((ret = regmap_read(ctx->regmap, MISTAT, &mistat) != 0) && - (mistat & BUSY)) - cpu_relax(); - - if (unlikely(ret)) - goto err_out; - - ret = regmap_write(ctx->regmap, MICMD, 0); - if (unlikely(ret)) - goto err_out; - - ret = regmap_read(ctx->regmap, MIRD, val); - -err_out: - if (ret) - pr_err("%s: error %d reading reg %02x\n", __func__, ret, - reg & PHREG_MASK); - - return ret; -} - -static int regmap_encx24j600_phy_reg_write(void *context, unsigned int reg, - unsigned int val) -{ - struct encx24j600_context *ctx = context; - int ret; - unsigned int mistat; - - reg = MIREGADR_VAL | (reg & PHREG_MASK); - ret = regmap_write(ctx->regmap, MIREGADR, reg); - if (unlikely(ret)) - goto err_out; - - ret = regmap_write(ctx->regmap, MIWR, val); - if (unlikely(ret)) - goto err_out; - - usleep_range(26, 100); - while ((ret = regmap_read(ctx->regmap, MISTAT, &mistat) != 0) && - (mistat & BUSY)) - cpu_relax(); - -err_out: - if (ret) - pr_err("%s: error %d writing reg %02x=%04x\n", __func__, ret, - reg & PHREG_MASK, val); - - return ret; -} - -static bool encx24j600_phymap_readable(struct device *dev, unsigned int reg) -{ - switch (reg) { - case PHCON1: - case PHSTAT1: - case PHANA: - case PHANLPA: - case PHANE: - case PHCON2: - case PHSTAT2: - case PHSTAT3: - return true; - default: - return false; - } -} - -static bool encx24j600_phymap_writeable(struct device *dev, unsigned int reg) -{ - switch (reg) { - case PHCON1: - case PHCON2: - case PHANA: - return true; - case PHSTAT1: - case PHSTAT2: - case PHSTAT3: - case PHANLPA: - case PHANE: - default: - return false; - } -} - -static bool encx24j600_phymap_volatile(struct device *dev, unsigned int reg) -{ - switch (reg) { - case PHSTAT1: - case PHSTAT2: - case PHSTAT3: - case PHANLPA: - case PHANE: - case PHCON2: - return true; - default: - return false; - } -} - -static struct regmap_config regcfg = { - .name = "reg", - .reg_bits = 8, - .val_bits = 16, - .max_register = 0xee, - .reg_stride = 2, - .cache_type = REGCACHE_RBTREE, - .val_format_endian = REGMAP_ENDIAN_LITTLE, - .readable_reg = encx24j600_regmap_readable, - .writeable_reg = encx24j600_regmap_writeable, - .volatile_reg = encx24j600_regmap_volatile, - .precious_reg = encx24j600_regmap_precious, - .lock = regmap_lock_mutex, - .unlock = regmap_unlock_mutex, -}; - -static struct regmap_bus regmap_encx24j600 = { - .write = regmap_encx24j600_write, - .read = regmap_encx24j600_read, - .reg_update_bits = regmap_encx24j600_reg_update_bits, -}; - -static struct regmap_config phycfg = { - .name = "phy", - .reg_bits = 8, - .val_bits = 16, - .max_register = 0x1f, - .cache_type = REGCACHE_RBTREE, - .val_format_endian = REGMAP_ENDIAN_LITTLE, - .readable_reg = encx24j600_phymap_readable, - .writeable_reg = encx24j600_phymap_writeable, - .volatile_reg = encx24j600_phymap_volatile, -}; -static struct regmap_bus phymap_encx24j600 = { - .reg_write = regmap_encx24j600_phy_reg_write, - .reg_read = regmap_encx24j600_phy_reg_read, -}; - -void devm_regmap_init_encx24j600(struct device *dev, - struct encx24j600_context *ctx) -{ - mutex_init(&ctx->mutex); - regcfg.lock_arg = ctx; - ctx->regmap = devm_regmap_init(dev, ®map_encx24j600, ctx, ®cfg); - ctx->phymap = devm_regmap_init(dev, &phymap_encx24j600, ctx, &phycfg); -} -EXPORT_SYMBOL_GPL(devm_regmap_init_encx24j600); - -MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/microchip/encx24j600.c b/drivers/net/ethernet/microchip/encx24j600.c deleted file mode 100644 index 1da37fd..0000000 --- a/drivers/net/ethernet/microchip/encx24j600.c +++ /dev/null @@ -1,1124 +0,0 @@ -/** - * Microchip ENCX24J600 ethernet driver - * - * Copyright (C) 2015 Gridpoint - * Author: Jon Ringle - * - * 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 "encx24j600_hw.h" - -#define DRV_NAME "encx24j600" -#define DRV_VERSION "1.0" - -#define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK) -static int debug = -1; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); - -/* SRAM memory layout: - * - * 0x0000-0x05ff TX buffers 1.5KB (1*1536) reside in the GP area in SRAM - * 0x0600-0x5fff RX buffers 22.5KB (15*1536) reside in the RX area in SRAM - */ -#define ENC_TX_BUF_START 0x0000U -#define ENC_RX_BUF_START 0x0600U -#define ENC_RX_BUF_END 0x5fffU -#define ENC_SRAM_SIZE 0x6000U - -enum { - RXFILTER_NORMAL, - RXFILTER_MULTI, - RXFILTER_PROMISC -}; - -struct encx24j600_priv { - struct net_device *ndev; - struct mutex lock; /* device access lock */ - struct encx24j600_context ctx; - struct sk_buff *tx_skb; - struct task_struct *kworker_task; - struct kthread_worker kworker; - struct kthread_work tx_work; - struct kthread_work setrx_work; - u16 next_packet; - bool hw_enabled; - bool full_duplex; - bool autoneg; - u16 speed; - int rxfilter; - u32 msg_enable; -}; - -static void dump_packet(const char *msg, int len, const char *data) -{ - pr_debug(DRV_NAME ": %s - packet len:%d\n", msg, len); - print_hex_dump_bytes("pk data: ", DUMP_PREFIX_OFFSET, data, len); -} - -static void encx24j600_dump_rsv(struct encx24j600_priv *priv, const char *msg, - struct rsv *rsv) -{ - struct net_device *dev = priv->ndev; - - netdev_info(dev, "RX packet Len:%d\n", rsv->len); - netdev_dbg(dev, "%s - NextPk: 0x%04x\n", msg, - rsv->next_packet); - netdev_dbg(dev, "RxOK: %d, DribbleNibble: %d\n", - RSV_GETBIT(rsv->rxstat, RSV_RXOK), - RSV_GETBIT(rsv->rxstat, RSV_DRIBBLENIBBLE)); - netdev_dbg(dev, "CRCErr:%d, LenChkErr: %d, LenOutOfRange: %d\n", - RSV_GETBIT(rsv->rxstat, RSV_CRCERROR), - RSV_GETBIT(rsv->rxstat, RSV_LENCHECKERR), - RSV_GETBIT(rsv->rxstat, RSV_LENOUTOFRANGE)); - netdev_dbg(dev, "Multicast: %d, Broadcast: %d, LongDropEvent: %d, CarrierEvent: %d\n", - RSV_GETBIT(rsv->rxstat, RSV_RXMULTICAST), - RSV_GETBIT(rsv->rxstat, RSV_RXBROADCAST), - RSV_GETBIT(rsv->rxstat, RSV_RXLONGEVDROPEV), - RSV_GETBIT(rsv->rxstat, RSV_CARRIEREV)); - netdev_dbg(dev, "ControlFrame: %d, PauseFrame: %d, UnknownOp: %d, VLanTagFrame: %d\n", - RSV_GETBIT(rsv->rxstat, RSV_RXCONTROLFRAME), - RSV_GETBIT(rsv->rxstat, RSV_RXPAUSEFRAME), - RSV_GETBIT(rsv->rxstat, RSV_RXUNKNOWNOPCODE), - RSV_GETBIT(rsv->rxstat, RSV_RXTYPEVLAN)); -} - -static u16 encx24j600_read_reg(struct encx24j600_priv *priv, u8 reg) -{ - struct net_device *dev = priv->ndev; - unsigned int val = 0; - int ret = regmap_read(priv->ctx.regmap, reg, &val); - if (unlikely(ret)) - netif_err(priv, drv, dev, "%s: error %d reading reg %02x\n", - __func__, ret, reg); - return val; -} - -static void encx24j600_write_reg(struct encx24j600_priv *priv, u8 reg, u16 val) -{ - struct net_device *dev = priv->ndev; - int ret = regmap_write(priv->ctx.regmap, reg, val); - if (unlikely(ret)) - netif_err(priv, drv, dev, "%s: error %d writing reg %02x=%04x\n", - __func__, ret, reg, val); -} - -static void encx24j600_update_reg(struct encx24j600_priv *priv, u8 reg, - u16 mask, u16 val) -{ - struct net_device *dev = priv->ndev; - int ret = regmap_update_bits(priv->ctx.regmap, reg, mask, val); - if (unlikely(ret)) - netif_err(priv, drv, dev, "%s: error %d updating reg %02x=%04x~%04x\n", - __func__, ret, reg, val, mask); -} - -static u16 encx24j600_read_phy(struct encx24j600_priv *priv, u8 reg) -{ - struct net_device *dev = priv->ndev; - unsigned int val = 0; - int ret = regmap_read(priv->ctx.phymap, reg, &val); - if (unlikely(ret)) - netif_err(priv, drv, dev, "%s: error %d reading %02x\n", - __func__, ret, reg); - return val; -} - -static void encx24j600_write_phy(struct encx24j600_priv *priv, u8 reg, u16 val) -{ - struct net_device *dev = priv->ndev; - int ret = regmap_write(priv->ctx.phymap, reg, val); - if (unlikely(ret)) - netif_err(priv, drv, dev, "%s: error %d writing reg %02x=%04x\n", - __func__, ret, reg, val); -} - -static void encx24j600_clr_bits(struct encx24j600_priv *priv, u8 reg, u16 mask) -{ - encx24j600_update_reg(priv, reg, mask, 0); -} - -static void encx24j600_set_bits(struct encx24j600_priv *priv, u8 reg, u16 mask) -{ - encx24j600_update_reg(priv, reg, mask, mask); -} - -static void encx24j600_cmd(struct encx24j600_priv *priv, u8 cmd) -{ - struct net_device *dev = priv->ndev; - int ret = regmap_write(priv->ctx.regmap, cmd, 0); - if (unlikely(ret)) - netif_err(priv, drv, dev, "%s: error %d with cmd %02x\n", - __func__, ret, cmd); -} - -static int encx24j600_raw_read(struct encx24j600_priv *priv, u8 reg, u8 *data, - size_t count) -{ - int ret; - mutex_lock(&priv->ctx.mutex); - ret = regmap_encx24j600_spi_read(&priv->ctx, reg, data, count); - mutex_unlock(&priv->ctx.mutex); - - return ret; -} - -static int encx24j600_raw_write(struct encx24j600_priv *priv, u8 reg, - const u8 *data, size_t count) -{ - int ret; - mutex_lock(&priv->ctx.mutex); - ret = regmap_encx24j600_spi_write(&priv->ctx, reg, data, count); - mutex_unlock(&priv->ctx.mutex); - - return ret; -} - -static void encx24j600_update_phcon1(struct encx24j600_priv *priv) -{ - u16 phcon1 = encx24j600_read_phy(priv, PHCON1); - if (priv->autoneg == AUTONEG_ENABLE) { - phcon1 |= ANEN | RENEG; - } else { - phcon1 &= ~ANEN; - if (priv->speed == SPEED_100) - phcon1 |= SPD100; - else - phcon1 &= ~SPD100; - - if (priv->full_duplex) - phcon1 |= PFULDPX; - else - phcon1 &= ~PFULDPX; - } - encx24j600_write_phy(priv, PHCON1, phcon1); -} - -/* Waits for autonegotiation to complete. */ -static int encx24j600_wait_for_autoneg(struct encx24j600_priv *priv) -{ - struct net_device *dev = priv->ndev; - unsigned long timeout = jiffies + msecs_to_jiffies(2000); - u16 phstat1; - u16 estat; - int ret = 0; - - phstat1 = encx24j600_read_phy(priv, PHSTAT1); - while ((phstat1 & ANDONE) == 0) { - if (time_after(jiffies, timeout)) { - u16 phstat3; - - netif_notice(priv, drv, dev, "timeout waiting for autoneg done\n"); - - priv->autoneg = AUTONEG_DISABLE; - phstat3 = encx24j600_read_phy(priv, PHSTAT3); - priv->speed = (phstat3 & PHY3SPD100) - ? SPEED_100 : SPEED_10; - priv->full_duplex = (phstat3 & PHY3DPX) ? 1 : 0; - encx24j600_update_phcon1(priv); - netif_notice(priv, drv, dev, "Using parallel detection: %s/%s", - priv->speed == SPEED_100 ? "100" : "10", - priv->full_duplex ? "Full" : "Half"); - - return -ETIMEDOUT; - } - cpu_relax(); - phstat1 = encx24j600_read_phy(priv, PHSTAT1); - } - - estat = encx24j600_read_reg(priv, ESTAT); - if (estat & PHYDPX) { - encx24j600_set_bits(priv, MACON2, FULDPX); - encx24j600_write_reg(priv, MABBIPG, 0x15); - } else { - encx24j600_clr_bits(priv, MACON2, FULDPX); - encx24j600_write_reg(priv, MABBIPG, 0x12); - /* Max retransmittions attempt */ - encx24j600_write_reg(priv, MACLCON, 0x370f); - } - - return ret; -} - -/* Access the PHY to determine link status */ -static void encx24j600_check_link_status(struct encx24j600_priv *priv) -{ - struct net_device *dev = priv->ndev; - u16 estat; - - estat = encx24j600_read_reg(priv, ESTAT); - - if (estat & PHYLNK) { - if (priv->autoneg == AUTONEG_ENABLE) - encx24j600_wait_for_autoneg(priv); - - netif_carrier_on(dev); - netif_info(priv, ifup, dev, "link up\n"); - } else { - netif_info(priv, ifdown, dev, "link down\n"); - - /* Re-enable autoneg since we won't know what we might be - * connected to when the link is brought back up again. - */ - priv->autoneg = AUTONEG_ENABLE; - priv->full_duplex = true; - priv->speed = SPEED_100; - netif_carrier_off(dev); - } -} - -static void encx24j600_int_link_handler(struct encx24j600_priv *priv) -{ - struct net_device *dev = priv->ndev; - - netif_dbg(priv, intr, dev, "%s", __func__); - encx24j600_check_link_status(priv); - encx24j600_clr_bits(priv, EIR, LINKIF); -} - -static void encx24j600_tx_complete(struct encx24j600_priv *priv, bool err) -{ - struct net_device *dev = priv->ndev; - - mutex_lock(&priv->lock); - - if (err) - dev->stats.tx_errors++; - else - dev->stats.tx_packets++; - - dev->stats.tx_bytes += priv->tx_skb->len; - - encx24j600_clr_bits(priv, EIR, TXIF | TXABTIF); - - netif_dbg(priv, tx_done, dev, "TX Done%s\n", err ? ": Err" : ""); - - if (priv->tx_skb) { - dev_kfree_skb(priv->tx_skb); - priv->tx_skb = NULL; - } - - netif_wake_queue(dev); - - mutex_unlock(&priv->lock); -} - -static int encx24j600_receive_packet(struct encx24j600_priv *priv, - struct rsv *rsv) -{ - struct net_device *dev = priv->ndev; - struct sk_buff *skb = netdev_alloc_skb(dev, rsv->len + NET_IP_ALIGN); - if (!skb) { - pr_err_ratelimited("RX: OOM: packet dropped\n"); - dev->stats.rx_dropped++; - return -ENOMEM; - } - skb_reserve(skb, NET_IP_ALIGN); - encx24j600_raw_read(priv, RRXDATA, skb_put(skb, rsv->len), rsv->len); - - if (netif_msg_pktdata(priv)) - dump_packet("RX", skb->len, skb->data); - - skb->dev = dev; - skb->protocol = eth_type_trans(skb, dev); - skb->ip_summed = CHECKSUM_COMPLETE; - - /* Maintain stats */ - dev->stats.rx_packets++; - dev->stats.rx_bytes += rsv->len; - priv->next_packet = rsv->next_packet; - - netif_rx(skb); - - return 0; -} - -static void encx24j600_rx_packets(struct encx24j600_priv *priv, u8 packet_count) -{ - struct net_device *dev = priv->ndev; - - while (packet_count--) { - struct rsv rsv; - u16 newrxtail; - - encx24j600_write_reg(priv, ERXRDPT, priv->next_packet); - encx24j600_raw_read(priv, RRXDATA, (u8 *)&rsv, sizeof(rsv)); - - if (netif_msg_rx_status(priv)) - encx24j600_dump_rsv(priv, __func__, &rsv); - - if (!RSV_GETBIT(rsv.rxstat, RSV_RXOK) || - (rsv.len > MAX_FRAMELEN)) { - netif_err(priv, rx_err, dev, "RX Error %04x\n", - rsv.rxstat); - dev->stats.rx_errors++; - - if (RSV_GETBIT(rsv.rxstat, RSV_CRCERROR)) - dev->stats.rx_crc_errors++; - if (RSV_GETBIT(rsv.rxstat, RSV_LENCHECKERR)) - dev->stats.rx_frame_errors++; - if (rsv.len > MAX_FRAMELEN) - dev->stats.rx_over_errors++; - } else { - encx24j600_receive_packet(priv, &rsv); - } - - newrxtail = priv->next_packet - 2; - if (newrxtail == ENC_RX_BUF_START) - newrxtail = SRAM_SIZE - 2; - - encx24j600_cmd(priv, SETPKTDEC); - encx24j600_write_reg(priv, ERXTAIL, newrxtail); - } -} - -static irqreturn_t encx24j600_isr(int irq, void *dev_id) -{ - struct encx24j600_priv *priv = dev_id; - struct net_device *dev = priv->ndev; - int eir; - - /* Clear interrupts */ - encx24j600_cmd(priv, CLREIE); - - eir = encx24j600_read_reg(priv, EIR); - - if (eir & LINKIF) - encx24j600_int_link_handler(priv); - - if (eir & TXIF) - encx24j600_tx_complete(priv, false); - - if (eir & TXABTIF) - encx24j600_tx_complete(priv, true); - - if (eir & RXABTIF) { - if (eir & PCFULIF) { - /* Packet counter is full */ - netif_err(priv, rx_err, dev, "Packet counter full\n"); - } - dev->stats.rx_dropped++; - encx24j600_clr_bits(priv, EIR, RXABTIF); - } - - if (eir & PKTIF) { - u8 packet_count; - - mutex_lock(&priv->lock); - - packet_count = encx24j600_read_reg(priv, ESTAT) & 0xff; - while (packet_count) { - encx24j600_rx_packets(priv, packet_count); - packet_count = encx24j600_read_reg(priv, ESTAT) & 0xff; - } - - mutex_unlock(&priv->lock); - } - - /* Enable interrupts */ - encx24j600_cmd(priv, SETEIE); - - return IRQ_HANDLED; -} - -static int encx24j600_soft_reset(struct encx24j600_priv *priv) -{ - int ret = 0; - int timeout; - u16 eudast; - - /* Write and verify a test value to EUDAST */ - regcache_cache_bypass(priv->ctx.regmap, true); - timeout = 10; - do { - encx24j600_write_reg(priv, EUDAST, EUDAST_TEST_VAL); - eudast = encx24j600_read_reg(priv, EUDAST); - usleep_range(25, 100); - } while ((eudast != EUDAST_TEST_VAL) && --timeout); - regcache_cache_bypass(priv->ctx.regmap, false); - - if (timeout == 0) { - ret = -ETIMEDOUT; - goto err_out; - } - - /* Wait for CLKRDY to become set */ - timeout = 10; - while (!(encx24j600_read_reg(priv, ESTAT) & CLKRDY) && --timeout) - usleep_range(25, 100); - - if (timeout == 0) { - ret = -ETIMEDOUT; - goto err_out; - } - - /* Issue a System Reset command */ - encx24j600_cmd(priv, SETETHRST); - usleep_range(25, 100); - - /* Confirm that EUDAST has 0000h after system reset */ - if (encx24j600_read_reg(priv, EUDAST) != 0) { - ret = -EINVAL; - goto err_out; - } - - /* Wait for PHY register and status bits to become available */ - usleep_range(256, 1000); - -err_out: - return ret; -} - -static int encx24j600_hw_reset(struct encx24j600_priv *priv) -{ - int ret; - - mutex_lock(&priv->lock); - ret = encx24j600_soft_reset(priv); - mutex_unlock(&priv->lock); - - return ret; -} - -static void encx24j600_reset_hw_tx(struct encx24j600_priv *priv) -{ - encx24j600_set_bits(priv, ECON2, TXRST); - encx24j600_clr_bits(priv, ECON2, TXRST); -} - -static void encx24j600_hw_init_tx(struct encx24j600_priv *priv) -{ - /* Reset TX */ - encx24j600_reset_hw_tx(priv); - - /* Clear the TXIF flag if were previously set */ - encx24j600_clr_bits(priv, EIR, TXIF | TXABTIF); - - /* Write the Tx Buffer pointer */ - encx24j600_write_reg(priv, EGPWRPT, ENC_TX_BUF_START); -} - -static void encx24j600_hw_init_rx(struct encx24j600_priv *priv) -{ - encx24j600_cmd(priv, DISABLERX); - - /* Set up RX packet start address in the SRAM */ - encx24j600_write_reg(priv, ERXST, ENC_RX_BUF_START); - - /* Preload the RX Data pointer to the beginning of the RX area */ - encx24j600_write_reg(priv, ERXRDPT, ENC_RX_BUF_START); - - priv->next_packet = ENC_RX_BUF_START; - - /* Set up RX end address in the SRAM */ - encx24j600_write_reg(priv, ERXTAIL, ENC_SRAM_SIZE - 2); - - /* Reset the user data pointers */ - encx24j600_write_reg(priv, EUDAST, ENC_SRAM_SIZE); - encx24j600_write_reg(priv, EUDAND, ENC_SRAM_SIZE + 1); - - /* Set Max Frame length */ - encx24j600_write_reg(priv, MAMXFL, MAX_FRAMELEN); -} - -static void encx24j600_dump_config(struct encx24j600_priv *priv, - const char *msg) -{ - pr_info(DRV_NAME ": %s\n", msg); - - /* CHIP configuration */ - pr_info(DRV_NAME " ECON1: %04X\n", encx24j600_read_reg(priv, ECON1)); - pr_info(DRV_NAME " ECON2: %04X\n", encx24j600_read_reg(priv, ECON2)); - pr_info(DRV_NAME " ERXFCON: %04X\n", encx24j600_read_reg(priv, - ERXFCON)); - pr_info(DRV_NAME " ESTAT: %04X\n", encx24j600_read_reg(priv, ESTAT)); - pr_info(DRV_NAME " EIR: %04X\n", encx24j600_read_reg(priv, EIR)); - pr_info(DRV_NAME " EIDLED: %04X\n", encx24j600_read_reg(priv, EIDLED)); - - /* MAC layer configuration */ - pr_info(DRV_NAME " MACON1: %04X\n", encx24j600_read_reg(priv, MACON1)); - pr_info(DRV_NAME " MACON2: %04X\n", encx24j600_read_reg(priv, MACON2)); - pr_info(DRV_NAME " MAIPG: %04X\n", encx24j600_read_reg(priv, MAIPG)); - pr_info(DRV_NAME " MACLCON: %04X\n", encx24j600_read_reg(priv, - MACLCON)); - pr_info(DRV_NAME " MABBIPG: %04X\n", encx24j600_read_reg(priv, - MABBIPG)); - - /* PHY configuation */ - pr_info(DRV_NAME " PHCON1: %04X\n", encx24j600_read_phy(priv, PHCON1)); - pr_info(DRV_NAME " PHCON2: %04X\n", encx24j600_read_phy(priv, PHCON2)); - pr_info(DRV_NAME " PHANA: %04X\n", encx24j600_read_phy(priv, PHANA)); - pr_info(DRV_NAME " PHANLPA: %04X\n", encx24j600_read_phy(priv, - PHANLPA)); - pr_info(DRV_NAME " PHANE: %04X\n", encx24j600_read_phy(priv, PHANE)); - pr_info(DRV_NAME " PHSTAT1: %04X\n", encx24j600_read_phy(priv, - PHSTAT1)); - pr_info(DRV_NAME " PHSTAT2: %04X\n", encx24j600_read_phy(priv, - PHSTAT2)); - pr_info(DRV_NAME " PHSTAT3: %04X\n", encx24j600_read_phy(priv, - PHSTAT3)); -} - -static void encx24j600_set_rxfilter_mode(struct encx24j600_priv *priv) -{ - switch (priv->rxfilter) { - case RXFILTER_PROMISC: - encx24j600_set_bits(priv, MACON1, PASSALL); - encx24j600_write_reg(priv, ERXFCON, UCEN | MCEN | NOTMEEN); - break; - case RXFILTER_MULTI: - encx24j600_clr_bits(priv, MACON1, PASSALL); - encx24j600_write_reg(priv, ERXFCON, UCEN | CRCEN | BCEN | MCEN); - break; - case RXFILTER_NORMAL: - default: - encx24j600_clr_bits(priv, MACON1, PASSALL); - encx24j600_write_reg(priv, ERXFCON, UCEN | CRCEN | BCEN); - break; - } -} - -static int encx24j600_hw_init(struct encx24j600_priv *priv) -{ - struct net_device *dev = priv->ndev; - int ret = 0; - u16 eidled; - u16 macon2; - - priv->hw_enabled = false; - - eidled = encx24j600_read_reg(priv, EIDLED); - if (((eidled & DEVID_MASK) >> DEVID_SHIFT) != ENCX24J600_DEV_ID) { - ret = -EINVAL; - goto err_out; - } - - netif_info(priv, drv, dev, "Silicon rev ID: 0x%02x\n", - (eidled & REVID_MASK) >> REVID_SHIFT); - - /* PHY Leds: link status, - * LEDA: Link + transmit/receive events - * LEDB: Link State + colision events - */ - encx24j600_update_reg(priv, EIDLED, 0xbc00, 0xbc00); - - /* Loopback disabled */ - encx24j600_write_reg(priv, MACON1, 0x9); - - /* interpacket gap value */ - encx24j600_write_reg(priv, MAIPG, 0x0c12); - - /* Write the auto negotiation pattern */ - encx24j600_write_phy(priv, PHANA, PHANA_DEFAULT); - - encx24j600_update_phcon1(priv); - encx24j600_check_link_status(priv); - - macon2 = MACON2_RSV1 | TXCRCEN | PADCFG0 | PADCFG2 | MACON2_DEFER; - if ((priv->autoneg == AUTONEG_DISABLE) && priv->full_duplex) - macon2 |= FULDPX; - - encx24j600_set_bits(priv, MACON2, macon2); - - priv->rxfilter = RXFILTER_NORMAL; - encx24j600_set_rxfilter_mode(priv); - - /* Program the Maximum frame length */ - encx24j600_write_reg(priv, MAMXFL, MAX_FRAMELEN); - - /* Init Tx pointers */ - encx24j600_hw_init_tx(priv); - - /* Init Rx pointers */ - encx24j600_hw_init_rx(priv); - - if (netif_msg_hw(priv)) - encx24j600_dump_config(priv, "Hw is initialized"); - -err_out: - return ret; -} - -static void encx24j600_hw_enable(struct encx24j600_priv *priv) -{ - /* Clear the interrupt flags in case was set */ - encx24j600_clr_bits(priv, EIR, (PCFULIF | RXABTIF | TXABTIF | TXIF | - PKTIF | LINKIF)); - - /* Enable the interrupts */ - encx24j600_write_reg(priv, EIE, (PCFULIE | RXABTIE | TXABTIE | TXIE | - PKTIE | LINKIE | INTIE)); - - /* Enable RX */ - encx24j600_cmd(priv, ENABLERX); - - priv->hw_enabled = true; -} - -static void encx24j600_hw_disable(struct encx24j600_priv *priv) -{ - /* Disable all interrupts */ - encx24j600_write_reg(priv, EIE, 0); - - /* Disable RX */ - encx24j600_cmd(priv, DISABLERX); - - priv->hw_enabled = false; -} - -static int encx24j600_setlink(struct net_device *dev, u8 autoneg, u16 speed, - u8 duplex) -{ - struct encx24j600_priv *priv = netdev_priv(dev); - int ret = 0; - - if (!priv->hw_enabled) { - /* link is in low power mode now; duplex setting - * will take effect on next encx24j600_hw_init() - */ - if (speed == SPEED_10 || speed == SPEED_100) { - priv->autoneg = (autoneg == AUTONEG_ENABLE); - priv->full_duplex = (duplex == DUPLEX_FULL); - priv->speed = (speed == SPEED_100); - } else { - netif_warn(priv, link, dev, "unsupported link speed setting\n"); - /*speeds other than SPEED_10 and SPEED_100 */ - /*are not supported by chip */ - ret = -EOPNOTSUPP; - } - } else { - netif_warn(priv, link, dev, "Warning: hw must be disabled to set link mode\n"); - ret = -EBUSY; - } - return ret; -} - -static void encx24j600_hw_get_macaddr(struct encx24j600_priv *priv, - unsigned char *ethaddr) -{ - unsigned short val; - - val = encx24j600_read_reg(priv, MAADR1); - - ethaddr[0] = val & 0x00ff; - ethaddr[1] = (val & 0xff00) >> 8; - - val = encx24j600_read_reg(priv, MAADR2); - - ethaddr[2] = val & 0x00ffU; - ethaddr[3] = (val & 0xff00U) >> 8; - - val = encx24j600_read_reg(priv, MAADR3); - - ethaddr[4] = val & 0x00ffU; - ethaddr[5] = (val & 0xff00U) >> 8; -} - -/* Program the hardware MAC address from dev->dev_addr.*/ -static int encx24j600_set_hw_macaddr(struct net_device *dev) -{ - struct encx24j600_priv *priv = netdev_priv(dev); - - if (priv->hw_enabled) { - netif_info(priv, drv, dev, "Hardware must be disabled to set Mac address\n"); - return -EBUSY; - } - - mutex_lock(&priv->lock); - - netif_info(priv, drv, dev, "%s: Setting MAC address to %pM\n", - dev->name, dev->dev_addr); - - encx24j600_write_reg(priv, MAADR3, (dev->dev_addr[4] | - dev->dev_addr[5] << 8)); - encx24j600_write_reg(priv, MAADR2, (dev->dev_addr[2] | - dev->dev_addr[3] << 8)); - encx24j600_write_reg(priv, MAADR1, (dev->dev_addr[0] | - dev->dev_addr[1] << 8)); - - mutex_unlock(&priv->lock); - - return 0; -} - -/* Store the new hardware address in dev->dev_addr, and update the MAC.*/ -static int encx24j600_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr *address = addr; - - if (netif_running(dev)) - return -EBUSY; - if (!is_valid_ether_addr(address->sa_data)) - return -EADDRNOTAVAIL; - - memcpy(dev->dev_addr, address->sa_data, dev->addr_len); - return encx24j600_set_hw_macaddr(dev); -} - -static int encx24j600_open(struct net_device *dev) -{ - struct encx24j600_priv *priv = netdev_priv(dev); - - int ret = request_threaded_irq(priv->ctx.spi->irq, NULL, encx24j600_isr, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - DRV_NAME, priv); - if (unlikely(ret < 0)) { - netdev_err(dev, "request irq %d failed (ret = %d)\n", - priv->ctx.spi->irq, ret); - return ret; - } - - encx24j600_hw_disable(priv); - encx24j600_hw_init(priv); - encx24j600_hw_enable(priv); - netif_start_queue(dev); - - return 0; -} - -static int encx24j600_stop(struct net_device *dev) -{ - struct encx24j600_priv *priv = netdev_priv(dev); - - netif_stop_queue(dev); - free_irq(priv->ctx.spi->irq, priv); - return 0; -} - -static void encx24j600_setrx_proc(struct kthread_work *ws) -{ - struct encx24j600_priv *priv = - container_of(ws, struct encx24j600_priv, setrx_work); - - mutex_lock(&priv->lock); - encx24j600_set_rxfilter_mode(priv); - mutex_unlock(&priv->lock); -} - -static void encx24j600_set_multicast_list(struct net_device *dev) -{ - struct encx24j600_priv *priv = netdev_priv(dev); - int oldfilter = priv->rxfilter; - - if (dev->flags & IFF_PROMISC) { - netif_dbg(priv, link, dev, "promiscuous mode\n"); - priv->rxfilter = RXFILTER_PROMISC; - } else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev)) { - netif_dbg(priv, link, dev, "%smulticast mode\n", - (dev->flags & IFF_ALLMULTI) ? "all-" : ""); - priv->rxfilter = RXFILTER_MULTI; - } else { - netif_dbg(priv, link, dev, "normal mode\n"); - priv->rxfilter = RXFILTER_NORMAL; - } - - if (oldfilter != priv->rxfilter) - queue_kthread_work(&priv->kworker, &priv->setrx_work); -} - -static void encx24j600_hw_tx(struct encx24j600_priv *priv) -{ - struct net_device *dev = priv->ndev; - netif_info(priv, tx_queued, dev, "TX Packet Len:%d\n", - priv->tx_skb->len); - - if (netif_msg_pktdata(priv)) - dump_packet("TX", priv->tx_skb->len, priv->tx_skb->data); - - if (encx24j600_read_reg(priv, EIR) & TXABTIF) - /* Last transmition aborted due to error. Reset TX interface */ - encx24j600_reset_hw_tx(priv); - - /* Clear the TXIF flag if were previously set */ - encx24j600_clr_bits(priv, EIR, TXIF); - - /* Set the data pointer to the TX buffer address in the SRAM */ - encx24j600_write_reg(priv, EGPWRPT, ENC_TX_BUF_START); - - /* Copy the packet into the SRAM */ - encx24j600_raw_write(priv, WGPDATA, (u8 *)priv->tx_skb->data, - priv->tx_skb->len); - - /* Program the Tx buffer start pointer */ - encx24j600_write_reg(priv, ETXST, ENC_TX_BUF_START); - - /* Program the packet length */ - encx24j600_write_reg(priv, ETXLEN, priv->tx_skb->len); - - /* Start the transmission */ - encx24j600_cmd(priv, SETTXRTS); -} - -static void encx24j600_tx_proc(struct kthread_work *ws) -{ - struct encx24j600_priv *priv = - container_of(ws, struct encx24j600_priv, tx_work); - - mutex_lock(&priv->lock); - encx24j600_hw_tx(priv); - mutex_unlock(&priv->lock); -} - -static netdev_tx_t encx24j600_tx(struct sk_buff *skb, struct net_device *dev) -{ - struct encx24j600_priv *priv = netdev_priv(dev); - - netif_stop_queue(dev); - - /* save the timestamp */ - dev->trans_start = jiffies; - - /* Remember the skb for deferred processing */ - priv->tx_skb = skb; - - queue_kthread_work(&priv->kworker, &priv->tx_work); - - return NETDEV_TX_OK; -} - -/* Deal with a transmit timeout */ -static void encx24j600_tx_timeout(struct net_device *dev) -{ - struct encx24j600_priv *priv = netdev_priv(dev); - - netif_err(priv, tx_err, dev, "TX timeout at %ld, latency %ld\n", - jiffies, jiffies - dev->trans_start); - - dev->stats.tx_errors++; - netif_wake_queue(dev); - return; -} - -static int encx24j600_get_regs_len(struct net_device *dev) -{ - return SFR_REG_COUNT; -} - -static void encx24j600_get_regs(struct net_device *dev, - struct ethtool_regs *regs, void *p) -{ - struct encx24j600_priv *priv = netdev_priv(dev); - u16 *buff = p; - u8 reg; - - regs->version = 1; - mutex_lock(&priv->lock); - for (reg = 0; reg < SFR_REG_COUNT; reg += 2) { - unsigned int val = 0; - /* ignore errors for unreadable registers */ - regmap_read(priv->ctx.regmap, reg, &val); - buff[reg] = val & 0xffff; - } - mutex_unlock(&priv->lock); -} - -static void encx24j600_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) -{ - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, dev_name(dev->dev.parent), - sizeof(info->bus_info)); -} - -static int encx24j600_get_settings(struct net_device *dev, - struct ethtool_cmd *cmd) -{ - struct encx24j600_priv *priv = netdev_priv(dev); - - cmd->transceiver = XCVR_INTERNAL; - cmd->supported = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | - SUPPORTED_Autoneg | SUPPORTED_TP; - - ethtool_cmd_speed_set(cmd, priv->speed); - cmd->duplex = priv->full_duplex ? DUPLEX_FULL : DUPLEX_HALF; - cmd->port = PORT_TP; - cmd->autoneg = priv->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE; - - return 0; -} - -static int encx24j600_set_settings(struct net_device *dev, - struct ethtool_cmd *cmd) -{ - return encx24j600_setlink(dev, cmd->autoneg, - ethtool_cmd_speed(cmd), cmd->duplex); -} - -static u32 encx24j600_get_msglevel(struct net_device *dev) -{ - struct encx24j600_priv *priv = netdev_priv(dev); - return priv->msg_enable; -} - -static void encx24j600_set_msglevel(struct net_device *dev, u32 val) -{ - struct encx24j600_priv *priv = netdev_priv(dev); - priv->msg_enable = val; -} - -static const struct ethtool_ops encx24j600_ethtool_ops = { - .get_settings = encx24j600_get_settings, - .set_settings = encx24j600_set_settings, - .get_drvinfo = encx24j600_get_drvinfo, - .get_msglevel = encx24j600_get_msglevel, - .set_msglevel = encx24j600_set_msglevel, - .get_regs_len = encx24j600_get_regs_len, - .get_regs = encx24j600_get_regs, -}; - -static const struct net_device_ops encx24j600_netdev_ops = { - .ndo_open = encx24j600_open, - .ndo_stop = encx24j600_stop, - .ndo_start_xmit = encx24j600_tx, - .ndo_set_rx_mode = encx24j600_set_multicast_list, - .ndo_set_mac_address = encx24j600_set_mac_address, - .ndo_tx_timeout = encx24j600_tx_timeout, - .ndo_validate_addr = eth_validate_addr, -}; - -static int encx24j600_spi_probe(struct spi_device *spi) -{ - int ret; - - struct net_device *ndev; - struct encx24j600_priv *priv; - - ndev = alloc_etherdev(sizeof(struct encx24j600_priv)); - - if (!ndev) { - ret = -ENOMEM; - goto error_out; - } - - priv = netdev_priv(ndev); - spi_set_drvdata(spi, priv); - dev_set_drvdata(&spi->dev, priv); - SET_NETDEV_DEV(ndev, &spi->dev); - - priv->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE); - priv->ndev = ndev; - - /* Default configuration PHY configuration */ - priv->full_duplex = true; - priv->autoneg = AUTONEG_ENABLE; - priv->speed = SPEED_100; - - priv->ctx.spi = spi; - devm_regmap_init_encx24j600(&spi->dev, &priv->ctx); - ndev->irq = spi->irq; - ndev->netdev_ops = &encx24j600_netdev_ops; - - mutex_init(&priv->lock); - - /* Reset device and check if it is connected */ - if (encx24j600_hw_reset(priv)) { - netif_err(priv, probe, ndev, - DRV_NAME ": Chip is not detected\n"); - ret = -EIO; - goto out_free; - } - - /* Initialize the device HW to the consistent state */ - if (encx24j600_hw_init(priv)) { - netif_err(priv, probe, ndev, - DRV_NAME ": HW initialization error\n"); - ret = -EIO; - goto out_free; - } - - init_kthread_worker(&priv->kworker); - init_kthread_work(&priv->tx_work, encx24j600_tx_proc); - init_kthread_work(&priv->setrx_work, encx24j600_setrx_proc); - - priv->kworker_task = kthread_run(kthread_worker_fn, &priv->kworker, - "encx24j600"); - - if (IS_ERR(priv->kworker_task)) { - ret = PTR_ERR(priv->kworker_task); - goto out_free; - } - - /* Get the MAC address from the chip */ - encx24j600_hw_get_macaddr(priv, ndev->dev_addr); - - ndev->ethtool_ops = &encx24j600_ethtool_ops; - - ret = register_netdev(ndev); - if (unlikely(ret)) { - netif_err(priv, probe, ndev, "Error %d initializing card encx24j600 card\n", - ret); - goto out_free; - } - - netif_info(priv, drv, priv->ndev, "MAC address %pM\n", ndev->dev_addr); - - return ret; - -out_free: - free_netdev(ndev); - -error_out: - return ret; -} - -static int encx24j600_spi_remove(struct spi_device *spi) -{ - struct encx24j600_priv *priv = dev_get_drvdata(&spi->dev); - - unregister_netdev(priv->ndev); - - free_netdev(priv->ndev); - - return 0; -} - -static const struct spi_device_id encx24j600_spi_id_table = { - .name = "encx24j600" -}; - -static struct spi_driver encx24j600_spi_net_driver = { - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - .bus = &spi_bus_type, - }, - .probe = encx24j600_spi_probe, - .remove = encx24j600_spi_remove, - .id_table = &encx24j600_spi_id_table, -}; - -static int __init encx24j600_init(void) -{ - return spi_register_driver(&encx24j600_spi_net_driver); -} -module_init(encx24j600_init); - -void encx24j600_exit(void) -{ - spi_unregister_driver(&encx24j600_spi_net_driver); -} -module_exit(encx24j600_exit); - -MODULE_DESCRIPTION(DRV_NAME " ethernet driver"); -MODULE_AUTHOR("Jon Ringle "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("spi:" DRV_NAME); diff --git a/drivers/net/ethernet/microchip/encx24j600_hw.h b/drivers/net/ethernet/microchip/encx24j600_hw.h deleted file mode 100644 index 4be73d5..0000000 --- a/drivers/net/ethernet/microchip/encx24j600_hw.h +++ /dev/null @@ -1,437 +0,0 @@ -/** - * encx24j600_hw.h: Register definitions - * - */ - -#ifndef _ENCX24J600_HW_H -#define _ENCX24J600_HW_H - -struct encx24j600_context { - struct spi_device *spi; - struct regmap *regmap; - struct regmap *phymap; - struct mutex mutex; /* mutex to protect access to regmap */ - int bank; -}; - -void devm_regmap_init_encx24j600(struct device *dev, - struct encx24j600_context *ctx); - -/* Single-byte instructions */ -#define BANK_SELECT(bank) (0xC0 | ((bank & (BANK_MASK >> BANK_SHIFT)) << 1)) -#define B0SEL 0xC0 /* Bank 0 Select */ -#define B1SEL 0xC2 /* Bank 1 Select */ -#define B2SEL 0xC4 /* Bank 2 Select */ -#define B3SEL 0xC6 /* Bank 3 Select */ -#define SETETHRST 0xCA /* System Reset */ -#define FCDISABLE 0xE0 /* Flow Control Disable */ -#define FCSINGLE 0xE2 /* Flow Control Single */ -#define FCMULTIPLE 0xE4 /* Flow Control Multiple */ -#define FCCLEAR 0xE6 /* Flow Control Clear */ -#define SETPKTDEC 0xCC /* Decrement Packet Counter */ -#define DMASTOP 0xD2 /* DMA Stop */ -#define DMACKSUM 0xD8 /* DMA Start Checksum */ -#define DMACKSUMS 0xDA /* DMA Start Checksum with Seed */ -#define DMACOPY 0xDC /* DMA Start Copy */ -#define DMACOPYS 0xDE /* DMA Start Copy and Checksum with Seed */ -#define SETTXRTS 0xD4 /* Request Packet Transmission */ -#define ENABLERX 0xE8 /* Enable RX */ -#define DISABLERX 0xEA /* Disable RX */ -#define SETEIE 0xEC /* Enable Interrupts */ -#define CLREIE 0xEE /* Disable Interrupts */ - -/* Two byte instructions */ -#define RBSEL 0xC8 /* Read Bank Select */ - -/* Three byte instructions */ -#define WGPRDPT 0x60 /* Write EGPRDPT */ -#define RGPRDPT 0x62 /* Read EGPRDPT */ -#define WRXRDPT 0x64 /* Write ERXRDPT */ -#define RRXRDPT 0x66 /* Read ERXRDPT */ -#define WUDARDPT 0x68 /* Write EUDARDPT */ -#define RUDARDPT 0x6A /* Read EUDARDPT */ -#define WGPWRPT 0x6C /* Write EGPWRPT */ -#define RGPWRPT 0x6E /* Read EGPWRPT */ -#define WRXWRPT 0x70 /* Write ERXWRPT */ -#define RRXWRPT 0x72 /* Read ERXWRPT */ -#define WUDAWRPT 0x74 /* Write EUDAWRPT */ -#define RUDAWRPT 0x76 /* Read EUDAWRPT */ - -/* n byte instructions */ -#define RCRCODE 0x00 -#define WCRCODE 0x40 -#define BFSCODE 0x80 -#define BFCCODE 0xA0 -#define RCR(addr) (RCRCODE | (addr & ADDR_MASK)) /* Read Control Register */ -#define WCR(addr) (WCRCODE | (addr & ADDR_MASK)) /* Write Control Register */ -#define RCRU 0x20 /* Read Control Register Unbanked */ -#define WCRU 0x22 /* Write Control Register Unbanked */ -#define BFS(addr) (BFSCODE | (addr & ADDR_MASK)) /* Bit Field Set */ -#define BFC(addr) (BFCCODE | (addr & ADDR_MASK)) /* Bit Field Clear */ -#define BFSU 0x24 /* Bit Field Set Unbanked */ -#define BFCU 0x26 /* Bit Field Clear Unbanked */ -#define RGPDATA 0x28 /* Read EGPDATA */ -#define WGPDATA 0x2A /* Write EGPDATA */ -#define RRXDATA 0x2C /* Read ERXDATA */ -#define WRXDATA 0x2E /* Write ERXDATA */ -#define RUDADATA 0x30 /* Read EUDADATA */ -#define WUDADATA 0x32 /* Write EUDADATA */ - -#define SFR_REG_COUNT 0xA0 - -/* ENC424J600 Control Registers - * Control register definitions are a combination of address - * and bank number - * - Register address (bits 0-4) - * - Bank number (bits 5-6) - */ -#define ADDR_MASK 0x1F -#define BANK_MASK 0x60 -#define BANK_SHIFT 5 - -/* All-bank registers */ -#define EUDAST 0x16 -#define EUDAND 0x18 -#define ESTAT 0x1A -#define EIR 0x1C -#define ECON1 0x1E - -/* Bank 0 registers */ -#define ETXST (0x00 | 0x00) -#define ETXLEN (0x02 | 0x00) -#define ERXST (0x04 | 0x00) -#define ERXTAIL (0x06 | 0x00) -#define ERXHEAD (0x08 | 0x00) -#define EDMAST (0x0A | 0x00) -#define EDMALEN (0x0C | 0x00) -#define EDMADST (0x0E | 0x00) -#define EDMACS (0x10 | 0x00) -#define ETXSTAT (0x12 | 0x00) -#define ETXWIRE (0x14 | 0x00) - -/* Bank 1 registers */ -#define EHT1 (0x00 | 0x20) -#define EHT2 (0x02 | 0x20) -#define EHT3 (0x04 | 0x20) -#define EHT4 (0x06 | 0x20) -#define EPMM1 (0x08 | 0x20) -#define EPMM2 (0x0A | 0x20) -#define EPMM3 (0x0C | 0x20) -#define EPMM4 (0x0E | 0x20) -#define EPMCS (0x10 | 0x20) -#define EPMO (0x12 | 0x20) -#define ERXFCON (0x14 | 0x20) - -/* Bank 2 registers */ -#define MACON1 (0x00 | 0x40) -#define MACON2 (0x02 | 0x40) -#define MABBIPG (0x04 | 0x40) -#define MAIPG (0x06 | 0x40) -#define MACLCON (0x08 | 0x40) -#define MAMXFL (0x0A | 0x40) -#define MICMD (0x12 | 0x40) -#define MIREGADR (0x14 | 0x40) - -/* Bank 3 registers */ -#define MAADR3 (0x00 | 0x60) -#define MAADR2 (0x02 | 0x60) -#define MAADR1 (0x04 | 0x60) -#define MIWR (0x06 | 0x60) -#define MIRD (0x08 | 0x60) -#define MISTAT (0x0A | 0x60) -#define EPAUS (0x0C | 0x60) -#define ECON2 (0x0E | 0x60) -#define ERXWM (0x10 | 0x60) -#define EIE (0x12 | 0x60) -#define EIDLED (0x14 | 0x60) - -/* Unbanked registers */ -#define EGPDATA (0x00 | 0x80) -#define ERXDATA (0x02 | 0x80) -#define EUDADATA (0x04 | 0x80) -#define EGPRDPT (0x06 | 0x80) -#define EGPWRPT (0x08 | 0x80) -#define ERXRDPT (0x0A | 0x80) -#define ERXWRPT (0x0C | 0x80) -#define EUDARDPT (0x0E | 0x80) -#define EUDAWRPT (0x10 | 0x80) - - -/* Register bit definitions */ -/* ESTAT */ -#define INT (1 << 15) -#define FCIDLE (1 << 14) -#define RXBUSY (1 << 13) -#define CLKRDY (1 << 12) -#define PHYDPX (1 << 10) -#define PHYLNK (1 << 8) - -/* EIR */ -#define CRYPTEN (1 << 15) -#define MODEXIF (1 << 14) -#define HASHIF (1 << 13) -#define AESIF (1 << 12) -#define LINKIF (1 << 11) -#define PKTIF (1 << 6) -#define DMAIF (1 << 5) -#define TXIF (1 << 3) -#define TXABTIF (1 << 2) -#define RXABTIF (1 << 1) -#define PCFULIF (1 << 0) - -/* ECON1 */ -#define MODEXST (1 << 15) -#define HASHEN (1 << 14) -#define HASHOP (1 << 13) -#define HASHLST (1 << 12) -#define AESST (1 << 11) -#define AESOP1 (1 << 10) -#define AESOP0 (1 << 9) -#define PKTDEC (1 << 8) -#define FCOP1 (1 << 7) -#define FCOP0 (1 << 6) -#define DMAST (1 << 5) -#define DMACPY (1 << 4) -#define DMACSSD (1 << 3) -#define DMANOCS (1 << 2) -#define TXRTS (1 << 1) -#define RXEN (1 << 0) - -/* ETXSTAT */ -#define LATECOL (1 << 10) -#define MAXCOL (1 << 9) -#define EXDEFER (1 << 8) -#define ETXSTATL_DEFER (1 << 7) -#define CRCBAD (1 << 4) -#define COLCNT_MASK 0xF - -/* ERXFCON */ -#define HTEN (1 << 15) -#define MPEN (1 << 14) -#define NOTPM (1 << 12) -#define PMEN3 (1 << 11) -#define PMEN2 (1 << 10) -#define PMEN1 (1 << 9) -#define PMEN0 (1 << 8) -#define CRCEEN (1 << 7) -#define CRCEN (1 << 6) -#define RUNTEEN (1 << 5) -#define RUNTEN (1 << 4) -#define UCEN (1 << 3) -#define NOTMEEN (1 << 2) -#define MCEN (1 << 1) -#define BCEN (1 << 0) - -/* MACON1 */ -#define LOOPBK (1 << 4) -#define RXPAUS (1 << 2) -#define PASSALL (1 << 1) - -/* MACON2 */ -#define MACON2_DEFER (1 << 14) -#define BPEN (1 << 13) -#define NOBKOFF (1 << 12) -#define PADCFG2 (1 << 7) -#define PADCFG1 (1 << 6) -#define PADCFG0 (1 << 5) -#define TXCRCEN (1 << 4) -#define PHDREN (1 << 3) -#define HFRMEN (1 << 2) -#define MACON2_RSV1 (1 << 1) -#define FULDPX (1 << 0) - -/* MAIPG */ -/* value of the high byte is given by the reserved bits, - * value of the low byte is recomended setting of the - * IPG parameter. - */ -#define MAIPGH_VAL 0x0C -#define MAIPGL_VAL 0x12 - -/* MIREGADRH */ -#define MIREGADR_VAL (1 << 8) - -/* MIREGADRL */ -#define PHREG_MASK 0x1F - -/* MICMD */ -#define MIISCAN (1 << 1) -#define MIIRD (1 << 0) - -/* MISTAT */ -#define NVALID (1 << 2) -#define SCAN (1 << 1) -#define BUSY (1 << 0) - -/* ECON2 */ -#define ETHEN (1 << 15) -#define STRCH (1 << 14) -#define TXMAC (1 << 13) -#define SHA1MD5 (1 << 12) -#define COCON3 (1 << 11) -#define COCON2 (1 << 10) -#define COCON1 (1 << 9) -#define COCON0 (1 << 8) -#define AUTOFC (1 << 7) -#define TXRST (1 << 6) -#define RXRST (1 << 5) -#define ETHRST (1 << 4) -#define MODLEN1 (1 << 3) -#define MODLEN0 (1 << 2) -#define AESLEN1 (1 << 1) -#define AESLEN0 (1 << 0) - -/* EIE */ -#define INTIE (1 << 15) -#define MODEXIE (1 << 14) -#define HASHIE (1 << 13) -#define AESIE (1 << 12) -#define LINKIE (1 << 11) -#define PKTIE (1 << 6) -#define DMAIE (1 << 5) -#define TXIE (1 << 3) -#define TXABTIE (1 << 2) -#define RXABTIE (1 << 1) -#define PCFULIE (1 << 0) - -/* EIDLED */ -#define LACFG3 (1 << 15) -#define LACFG2 (1 << 14) -#define LACFG1 (1 << 13) -#define LACFG0 (1 << 12) -#define LBCFG3 (1 << 11) -#define LBCFG2 (1 << 10) -#define LBCFG1 (1 << 9) -#define LBCFG0 (1 << 8) -#define DEVID_SHIFT 5 -#define DEVID_MASK (0x7 << DEVID_SHIFT) -#define REVID_SHIFT 0 -#define REVID_MASK (0x1F << REVID_SHIFT) - -/* PHY registers */ -#define PHCON1 0x00 -#define PHSTAT1 0x01 -#define PHANA 0x04 -#define PHANLPA 0x05 -#define PHANE 0x06 -#define PHCON2 0x11 -#define PHSTAT2 0x1B -#define PHSTAT3 0x1F - -/* PHCON1 */ -#define PRST (1 << 15) -#define PLOOPBK (1 << 14) -#define SPD100 (1 << 13) -#define ANEN (1 << 12) -#define PSLEEP (1 << 11) -#define RENEG (1 << 9) -#define PFULDPX (1 << 8) - -/* PHSTAT1 */ -#define FULL100 (1 << 14) -#define HALF100 (1 << 13) -#define FULL10 (1 << 12) -#define HALF10 (1 << 11) -#define ANDONE (1 << 5) -#define LRFAULT (1 << 4) -#define ANABLE (1 << 3) -#define LLSTAT (1 << 2) -#define EXTREGS (1 << 0) - -/* PHSTAT2 */ -#define PLRITY (1 << 4) - -/* PHSTAT3 */ -#define PHY3SPD100 (1 << 3) -#define PHY3DPX (1 << 4) -#define SPDDPX_SHIFT 2 -#define SPDDPX_MASK (0x7 << SPDDPX_SHIFT) - -/* PHANA */ -/* Default value for PHY initialization*/ -#define PHANA_DEFAULT 0x05E1 - -/* PHANE */ -#define PDFLT (1 << 4) -#define LPARCD (1 << 1) -#define LPANABL (1 << 0) - -#define EUDAST_TEST_VAL 0x1234 - -#define TSV_SIZE 7 - -#define ENCX24J600_DEV_ID 0x1 - -/* Configuration */ - -/* Led is on when the link is present and driven low - * temporarily when packet is TX'd or RX'd - */ -#define LED_A_SETTINGS 0xC - -/* Led is on if the link is in 100 Mbps mode */ -#define LED_B_SETTINGS 0x8 - -/* maximum ethernet frame length - * Currently not used as a limit anywhere - * (we're using the "huge frame enable" feature of - * enc424j600). - */ -#define MAX_FRAMELEN 1518 - -/* Size in bytes of the receive buffer in enc424j600. - * Must be word aligned (even). - */ -#define RX_BUFFER_SIZE (15 * MAX_FRAMELEN) - -/* Start of the general purpose area in sram */ -#define SRAM_GP_START 0x0 - -/* SRAM size */ -#define SRAM_SIZE 0x6000 - -/* Start of the receive buffer */ -#define ERXST_VAL (SRAM_SIZE - RX_BUFFER_SIZE) - -#define RSV_RXLONGEVDROPEV 16 -#define RSV_CARRIEREV 18 -#define RSV_CRCERROR 20 -#define RSV_LENCHECKERR 21 -#define RSV_LENOUTOFRANGE 22 -#define RSV_RXOK 23 -#define RSV_RXMULTICAST 24 -#define RSV_RXBROADCAST 25 -#define RSV_DRIBBLENIBBLE 26 -#define RSV_RXCONTROLFRAME 27 -#define RSV_RXPAUSEFRAME 28 -#define RSV_RXUNKNOWNOPCODE 29 -#define RSV_RXTYPEVLAN 30 - -#define RSV_RUNTFILTERMATCH 31 -#define RSV_NOTMEFILTERMATCH 32 -#define RSV_HASHFILTERMATCH 33 -#define RSV_MAGICPKTFILTERMATCH 34 -#define RSV_PTRNMTCHFILTERMATCH 35 -#define RSV_UNICASTFILTERMATCH 36 - -#define RSV_SIZE 8 -#define RSV_BITMASK(x) (1 << ((x) - 16)) -#define RSV_GETBIT(x, y) (((x) & RSV_BITMASK(y)) ? 1 : 0) - -struct rsv { - u16 next_packet; - u16 len; - u32 rxstat; -}; - -/* Put RX buffer at 0 as suggested by the Errata datasheet */ - -#define RXSTART_INIT ERXST_VAL -#define RXEND_INIT 0x5FFF - -int regmap_encx24j600_spi_write(void *context, u8 reg, const u8 *data, - size_t count); -int regmap_encx24j600_spi_read(void *context, u8 reg, u8 *data, size_t count); - - -#endif -- cgit v0.10.2 From 21c4c073f14509d685ed219aa3c76362a7bfa0ac Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 6 Oct 2015 06:25:43 -0700 Subject: Revert "regmap: Allow installing custom reg_update_bits function" This reverts commit 7741c373cf3ea1f5383fa97fb7a640a429d3dd7c. diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 4036d7a..cc55788 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -98,9 +98,6 @@ struct regmap { int (*reg_read)(void *context, unsigned int reg, unsigned int *val); int (*reg_write)(void *context, unsigned int reg, unsigned int val); - int (*reg_update_bits)(void *context, unsigned int reg, - unsigned int mask, unsigned int val, - bool *change, bool force_write); bool defer_caching; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 70387c9..afaf562 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -619,7 +619,6 @@ struct regmap *__regmap_init(struct device *dev, goto skip_format_initialization; } else { map->reg_read = _regmap_bus_read; - map->reg_update_bits = bus->reg_update_bits; } reg_endian = regmap_get_reg_endian(bus, config); @@ -2510,30 +2509,6 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, int ret; unsigned int tmp, orig; - if (map->reg_update_bits) { - ret = map->reg_update_bits(map->bus_context, reg, mask, val, - change, force_write); - if (ret != 0) - return ret; - - /* Fix up the cache by read/modify/write */ - if (!map->cache_bypass && !map->defer_caching) { - ret = regcache_read(map, reg, &orig); - if (ret != 0) - return ret; - - tmp = orig & ~mask; - tmp |= val & mask; - - ret = regcache_write(map, reg, tmp); - if (ret != 0) - return ret; - if (map->cache_only) - map->cache_dirty = true; - } - return ret; - } - ret = _regmap_read(map, reg, &orig); if (ret != 0) return ret; diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 4d3a3b1..8fc0bfd 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -296,9 +296,6 @@ typedef int (*regmap_hw_reg_read)(void *context, unsigned int reg, unsigned int *val); typedef int (*regmap_hw_reg_write)(void *context, unsigned int reg, unsigned int val); -typedef int (*regmap_hw_reg_update_bits)(void *context, unsigned int reg, - unsigned int mask, unsigned int val, - bool *change, bool force_write); typedef struct regmap_async *(*regmap_hw_async_alloc)(void); typedef void (*regmap_hw_free_context)(void *context); @@ -338,7 +335,6 @@ struct regmap_bus { regmap_hw_gather_write gather_write; regmap_hw_async_write async_write; regmap_hw_reg_write reg_write; - regmap_hw_reg_update_bits reg_update_bits; regmap_hw_read read; regmap_hw_reg_read reg_read; regmap_hw_free_context free_context; -- cgit v0.10.2 From 77792b11409c9270d98e604b4314b85ce886ac7d Mon Sep 17 00:00:00 2001 From: Jon Ringle Date: Thu, 1 Oct 2015 12:38:07 -0400 Subject: regmap: Allow installing custom reg_update_bits function This commit allows installing a custom reg_update_bits function for cases where the hardware provides a mechanism to set or clear register bits without a read/modify/write cycle. Such is the case with the Microchip ENCX24J600. If a custom reg_update_bits function is provided, it will only be used against volatile registers. Signed-off-by: Jon Ringle Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index cc55788..628ad7a 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -98,6 +98,8 @@ struct regmap { int (*reg_read)(void *context, unsigned int reg, unsigned int *val); int (*reg_write)(void *context, unsigned int reg, unsigned int val); + int (*reg_update_bits)(void *context, unsigned int reg, + unsigned int mask, unsigned int val); bool defer_caching; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index afaf562..8cd155a 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -619,6 +619,7 @@ struct regmap *__regmap_init(struct device *dev, goto skip_format_initialization; } else { map->reg_read = _regmap_bus_read; + map->reg_update_bits = bus->reg_update_bits; } reg_endian = regmap_get_reg_endian(bus, config); @@ -2509,20 +2510,26 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, int ret; unsigned int tmp, orig; - ret = _regmap_read(map, reg, &orig); - if (ret != 0) - return ret; + if (change) + *change = false; - tmp = orig & ~mask; - tmp |= val & mask; - - if (force_write || (tmp != orig)) { - ret = _regmap_write(map, reg, tmp); - if (change) + if (regmap_volatile(map, reg) && map->reg_update_bits) { + ret = map->reg_update_bits(map->bus_context, reg, mask, val); + if (ret == 0 && change) *change = true; } else { - if (change) - *change = false; + ret = _regmap_read(map, reg, &orig); + if (ret != 0) + return ret; + + tmp = orig & ~mask; + tmp |= val & mask; + + if (force_write || (tmp != orig)) { + ret = _regmap_write(map, reg, tmp); + if (ret == 0 && change) + *change = true; + } } return ret; diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 8fc0bfd..b49d413 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -296,6 +296,8 @@ typedef int (*regmap_hw_reg_read)(void *context, unsigned int reg, unsigned int *val); typedef int (*regmap_hw_reg_write)(void *context, unsigned int reg, unsigned int val); +typedef int (*regmap_hw_reg_update_bits)(void *context, unsigned int reg, + unsigned int mask, unsigned int val); typedef struct regmap_async *(*regmap_hw_async_alloc)(void); typedef void (*regmap_hw_free_context)(void *context); @@ -335,6 +337,7 @@ struct regmap_bus { regmap_hw_gather_write gather_write; regmap_hw_async_write async_write; regmap_hw_reg_write reg_write; + regmap_hw_reg_update_bits reg_update_bits; regmap_hw_read read; regmap_hw_reg_read reg_read; regmap_hw_free_context free_context; -- cgit v0.10.2 From 9bc63ca0904da3f163c01087e27952dd962d548d Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 5 Oct 2015 12:43:57 +0200 Subject: Bluetooth: btbcm: Read the local name in setup stage The Broadcom Bluetooth controllers have the chip name included in the ROM firmware or later in the patchram firmware. For debugging purposes read the local name and print it out. This is only done during setup stage and only once before loading the firmware and once after loading the firmware. For the Broadcom based controllers from Apple, the name is only read once after determining the chip id. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c index ad7371d..2fc363a 100644 --- a/drivers/bluetooth/btbcm.c +++ b/drivers/bluetooth/btbcm.c @@ -181,6 +181,27 @@ static int btbcm_reset(struct hci_dev *hdev) return 0; } +static struct sk_buff *btbcm_read_local_name(struct hci_dev *hdev) +{ + struct sk_buff *skb; + + skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + BT_ERR("%s: BCM: Reading local name failed (%ld)", + hdev->name, PTR_ERR(skb)); + return skb; + } + + if (skb->len != sizeof(struct hci_rp_read_local_name)) { + BT_ERR("%s: BCM: Local name length mismatch", hdev->name); + kfree_skb(skb); + return ERR_PTR(-EIO); + } + + return skb; +} + static struct sk_buff *btbcm_read_local_version(struct hci_dev *hdev) { struct sk_buff *skb; @@ -393,6 +414,14 @@ int btbcm_setup_patchram(struct hci_dev *hdev) BT_INFO("%s: BCM: chip id %u", hdev->name, skb->data[1]); kfree_skb(skb); + /* Read Local Name */ + skb = btbcm_read_local_name(hdev); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1)); + kfree_skb(skb); + switch ((rev & 0xf000) >> 12) { case 0: case 3: @@ -464,6 +493,14 @@ int btbcm_setup_patchram(struct hci_dev *hdev) hw_name ? : "BCM", (subver & 0x7000) >> 13, (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff); + /* Read Local Name */ + skb = btbcm_read_local_name(hdev); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1)); + kfree_skb(skb); + btbcm_check_bdaddr(hdev); set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); @@ -490,6 +527,13 @@ int btbcm_setup_apple(struct hci_dev *hdev) kfree_skb(skb); } + /* Read Local Name */ + skb = btbcm_read_local_name(hdev); + if (!IS_ERR(skb)) { + BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1)); + kfree_skb(skb); + } + set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); return 0; -- cgit v0.10.2 From 4bac50bace0377138fed2ac3627c1ad470ea1eca Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 3 Oct 2015 18:09:01 +0100 Subject: net: dsa: mv88e6xxx: remove link polling The link status is polled by the generic phy layer, there's no need to duplicate that polling with additional polling. This additional polling adds additional MDIO traffic, and races with the generic phy layer, resulting in missing or duplicated link status messages. Tested-by: Andrew Lunn Signed-off-by: Russell King Signed-off-by: David S. Miller diff --git a/drivers/net/dsa/mv88e6123_61_65.c b/drivers/net/dsa/mv88e6123_61_65.c index 3de2a6d..4bcfd68 100644 --- a/drivers/net/dsa/mv88e6123_61_65.c +++ b/drivers/net/dsa/mv88e6123_61_65.c @@ -125,7 +125,6 @@ struct dsa_switch_driver mv88e6123_61_65_switch_driver = { .set_addr = mv88e6xxx_set_addr_indirect, .phy_read = mv88e6xxx_phy_read, .phy_write = mv88e6xxx_phy_write, - .poll_link = mv88e6xxx_poll_link, .get_strings = mv88e6xxx_get_strings, .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, .get_sset_count = mv88e6xxx_get_sset_count, diff --git a/drivers/net/dsa/mv88e6131.c b/drivers/net/dsa/mv88e6131.c index 3e83865..c73121c 100644 --- a/drivers/net/dsa/mv88e6131.c +++ b/drivers/net/dsa/mv88e6131.c @@ -178,7 +178,6 @@ struct dsa_switch_driver mv88e6131_switch_driver = { .set_addr = mv88e6xxx_set_addr_direct, .phy_read = mv88e6131_phy_read, .phy_write = mv88e6131_phy_write, - .poll_link = mv88e6xxx_poll_link, .get_strings = mv88e6xxx_get_strings, .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, .get_sset_count = mv88e6xxx_get_sset_count, diff --git a/drivers/net/dsa/mv88e6171.c b/drivers/net/dsa/mv88e6171.c index c2daaf0..c95cfab 100644 --- a/drivers/net/dsa/mv88e6171.c +++ b/drivers/net/dsa/mv88e6171.c @@ -104,7 +104,6 @@ struct dsa_switch_driver mv88e6171_switch_driver = { .set_addr = mv88e6xxx_set_addr_indirect, .phy_read = mv88e6xxx_phy_read_indirect, .phy_write = mv88e6xxx_phy_write_indirect, - .poll_link = mv88e6xxx_poll_link, .get_strings = mv88e6xxx_get_strings, .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, .get_sset_count = mv88e6xxx_get_sset_count, diff --git a/drivers/net/dsa/mv88e6352.c b/drivers/net/dsa/mv88e6352.c index 1f5129c..3736706 100644 --- a/drivers/net/dsa/mv88e6352.c +++ b/drivers/net/dsa/mv88e6352.c @@ -324,7 +324,6 @@ struct dsa_switch_driver mv88e6352_switch_driver = { .set_addr = mv88e6xxx_set_addr_indirect, .phy_read = mv88e6xxx_phy_read_indirect, .phy_write = mv88e6xxx_phy_write_indirect, - .poll_link = mv88e6xxx_poll_link, .get_strings = mv88e6xxx_get_strings, .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, .get_sset_count = mv88e6xxx_get_sset_count, diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index 932fb72..8e088e3 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -388,73 +388,6 @@ int mv88e6xxx_phy_write_ppu(struct dsa_switch *ds, int addr, } #endif -void mv88e6xxx_poll_link(struct dsa_switch *ds) -{ - int i; - - for (i = 0; i < DSA_MAX_PORTS; i++) { - struct net_device *dev; - int uninitialized_var(port_status); - int pcs_ctrl; - int link; - int speed; - int duplex; - int fc; - - dev = ds->ports[i]; - if (dev == NULL) - continue; - - pcs_ctrl = mv88e6xxx_reg_read(ds, REG_PORT(i), PORT_PCS_CTRL); - if (pcs_ctrl < 0 || pcs_ctrl & PORT_PCS_CTRL_FORCE_LINK) - continue; - - link = 0; - if (dev->flags & IFF_UP) { - port_status = mv88e6xxx_reg_read(ds, REG_PORT(i), - PORT_STATUS); - if (port_status < 0) - continue; - - link = !!(port_status & PORT_STATUS_LINK); - } - - if (!link) { - if (netif_carrier_ok(dev)) { - netdev_info(dev, "link down\n"); - netif_carrier_off(dev); - } - continue; - } - - switch (port_status & PORT_STATUS_SPEED_MASK) { - case PORT_STATUS_SPEED_10: - speed = 10; - break; - case PORT_STATUS_SPEED_100: - speed = 100; - break; - case PORT_STATUS_SPEED_1000: - speed = 1000; - break; - default: - speed = -1; - break; - } - duplex = (port_status & PORT_STATUS_DUPLEX) ? 1 : 0; - fc = (port_status & PORT_STATUS_PAUSE_EN) ? 1 : 0; - - if (!netif_carrier_ok(dev)) { - netdev_info(dev, - "link up, %d Mb/s, %s duplex, flow control %sabled\n", - speed, - duplex ? "full" : "half", - fc ? "en" : "dis"); - netif_carrier_on(dev); - } - } -} - static bool mv88e6xxx_6065_family(struct dsa_switch *ds) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h index 1cf3cf8..d8ec487 100644 --- a/drivers/net/dsa/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx.h @@ -442,7 +442,6 @@ void mv88e6xxx_ppu_state_init(struct dsa_switch *ds); int mv88e6xxx_phy_read_ppu(struct dsa_switch *ds, int addr, int regnum); int mv88e6xxx_phy_write_ppu(struct dsa_switch *ds, int addr, int regnum, u16 val); -void mv88e6xxx_poll_link(struct dsa_switch *ds); void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data); void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data); -- cgit v0.10.2 From d25b8e74291fec2dbf3fe3df7f20289eeaa9d28f Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 3 Oct 2015 18:09:07 +0100 Subject: net: dsa: better error reporting Add additional error reporting to the generic DSA code, so it's easier to debug when things go wrong. This was useful when initially bringing up 88e6176 on a new board. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Acked-by: Florian Fainelli Signed-off-by: David S. Miller diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index c59fa5d..aa398bc 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -326,8 +326,8 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent) ret = dsa_slave_create(ds, parent, i, pd->port_names[i]); if (ret < 0) { - netdev_err(dst->master_netdev, "[%d]: can't create dsa slave device for port %d(%s)\n", - index, i, pd->port_names[i]); + netdev_err(dst->master_netdev, "[%d]: can't create dsa slave device for port %d(%s): %d\n", + index, i, pd->port_names[i], ret); ret = 0; } } diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 5f65f92..4f607bc 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -1026,8 +1026,10 @@ static int dsa_slave_phy_connect(struct dsa_slave_priv *p, struct dsa_switch *ds = p->parent; p->phy = ds->slave_mii_bus->phy_map[addr]; - if (!p->phy) + if (!p->phy) { + netdev_err(slave_dev, "no phy at %d\n", addr); return -ENODEV; + } /* Use already configured phy mode */ if (p->phy_interface == PHY_INTERFACE_MODE_NA) @@ -1061,7 +1063,7 @@ static int dsa_slave_phy_setup(struct dsa_slave_priv *p, */ ret = of_phy_register_fixed_link(port_dn); if (ret) { - netdev_err(slave_dev, "failed to register fixed PHY\n"); + netdev_err(slave_dev, "failed to register fixed PHY: %d\n", ret); return ret; } phy_is_fixed = true; @@ -1072,17 +1074,20 @@ static int dsa_slave_phy_setup(struct dsa_slave_priv *p, phy_flags = ds->drv->get_phy_flags(ds, p->port); if (phy_dn) { - ret = of_mdio_parse_addr(&slave_dev->dev, phy_dn); + int phy_id = of_mdio_parse_addr(&slave_dev->dev, phy_dn); + /* If this PHY address is part of phys_mii_mask, which means * that we need to divert reads and writes to/from it, then we * want to bind this device using the slave MII bus created by * DSA to make that happen. */ - if (!phy_is_fixed && ret >= 0 && - (ds->phys_mii_mask & (1 << ret))) { - ret = dsa_slave_phy_connect(p, slave_dev, ret); - if (ret) + if (!phy_is_fixed && phy_id >= 0 && + (ds->phys_mii_mask & (1 << phy_id))) { + ret = dsa_slave_phy_connect(p, slave_dev, phy_id); + if (ret) { + netdev_err(slave_dev, "failed to connect to phy%d: %d\n", phy_id, ret); return ret; + } } else { p->phy = of_phy_connect(slave_dev, phy_dn, dsa_slave_adjust_link, @@ -1099,8 +1104,10 @@ static int dsa_slave_phy_setup(struct dsa_slave_priv *p, */ if (!p->phy) { ret = dsa_slave_phy_connect(p, slave_dev, p->port); - if (ret) + if (ret) { + netdev_err(slave_dev, "failed to connect to port %d: %d\n", p->port, ret); return ret; + } } else { netdev_info(slave_dev, "attached PHY at address %d [%s]\n", p->phy->addr, p->phy->drv->name); @@ -1212,6 +1219,7 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent, ret = dsa_slave_phy_setup(p, slave_dev); if (ret) { + netdev_err(master, "error %d setting up slave phy\n", ret); free_netdev(slave_dev); return ret; } -- cgit v0.10.2 From 16660f0bd942cec203eaf4de0e2ac1695bd9d32d Mon Sep 17 00:00:00 2001 From: David Ahern Date: Sat, 3 Oct 2015 11:43:46 -0700 Subject: net: Add support for filtering neigh dump by device index Add support for filtering neighbor dumps by device by adding the NDA_IFINDEX attribute to the dump request. Signed-off-by: David Ahern Reviewed-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 8c57fdf..1aa8437 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -2249,6 +2249,14 @@ static bool neigh_master_filtered(struct net_device *dev, int master_idx) return false; } +static bool neigh_ifindex_filtered(struct net_device *dev, int filter_idx) +{ + if (filter_idx && dev->ifindex != filter_idx) + return true; + + return false; +} + static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, struct netlink_callback *cb) { @@ -2259,16 +2267,19 @@ static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, int rc, h, s_h = cb->args[1]; int idx, s_idx = idx = cb->args[2]; struct neigh_hash_table *nht; - int filter_master_idx = 0; + int filter_master_idx = 0, filter_idx = 0; unsigned int flags = NLM_F_MULTI; int err; err = nlmsg_parse(nlh, sizeof(struct ndmsg), tb, NDA_MAX, NULL); if (!err) { + if (tb[NDA_IFINDEX]) + filter_idx = nla_get_u32(tb[NDA_IFINDEX]); + if (tb[NDA_MASTER]) filter_master_idx = nla_get_u32(tb[NDA_MASTER]); - if (filter_master_idx) + if (filter_idx || filter_master_idx) flags |= NLM_F_DUMP_FILTERED; } @@ -2283,6 +2294,8 @@ static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, 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) -- cgit v0.10.2 From 4917a1548ff41e53d863d6845b4da1884e4282b4 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Mon, 5 Oct 2015 12:11:21 +0200 Subject: bridge: netlink: make br_fill_info's frame size smaller When KASAN is enabled the frame size grows > 2048 bytes and we get a warning, so make it smaller. net/bridge/br_netlink.c: In function 'br_fill_info': >> net/bridge/br_netlink.c:1110:1: warning: the frame size of 2160 bytes >> is larger than 2048 bytes [-Wframe-larger-than=] Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 70efe2e..330abf4 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -1057,28 +1057,27 @@ static size_t br_get_size(const struct net_device *brdev) static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) { struct net_bridge *br = netdev_priv(brdev); - u64 hello_timer, tcn_timer, topology_change_timer, gc_timer, clockval; u32 forward_delay = jiffies_to_clock_t(br->forward_delay); u32 hello_time = jiffies_to_clock_t(br->hello_time); u32 age_time = jiffies_to_clock_t(br->max_age); u32 ageing_time = jiffies_to_clock_t(br->ageing_time); u32 stp_enabled = br->stp_enabled; u16 priority = (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1]; - u16 group_fwd_mask = br->group_fwd_mask; u8 vlan_enabled = br_vlan_enabled(br); - struct ifla_bridge_id root_id, bridge_id; - - memset(&bridge_id, 0, sizeof(bridge_id)); - memset(&root_id, 0, sizeof(root_id)); - memcpy(root_id.prio, br->designated_root.prio, sizeof(root_id.prio)); - memcpy(root_id.addr, br->designated_root.addr, sizeof(root_id.addr)); - memcpy(bridge_id.prio, br->bridge_id.prio, sizeof(bridge_id.prio)); - memcpy(bridge_id.addr, br->bridge_id.addr, sizeof(bridge_id.addr)); - hello_timer = br_timer_value(&br->hello_timer); - tcn_timer = br_timer_value(&br->tcn_timer); - topology_change_timer = br_timer_value(&br->topology_change_timer); - gc_timer = br_timer_value(&br->gc_timer); - clockval = 0; + u64 clockval; + + clockval = br_timer_value(&br->hello_timer); + if (nla_put_u64(skb, IFLA_BR_HELLO_TIMER, clockval)) + return -EMSGSIZE; + clockval = br_timer_value(&br->tcn_timer); + if (nla_put_u64(skb, IFLA_BR_TCN_TIMER, clockval)) + return -EMSGSIZE; + clockval = br_timer_value(&br->topology_change_timer); + if (nla_put_u64(skb, IFLA_BR_TOPOLOGY_CHANGE_TIMER, clockval)) + return -EMSGSIZE; + clockval = br_timer_value(&br->gc_timer); + if (nla_put_u64(skb, IFLA_BR_GC_TIMER, clockval)) + return -EMSGSIZE; if (nla_put_u32(skb, IFLA_BR_FORWARD_DELAY, forward_delay) || nla_put_u32(skb, IFLA_BR_HELLO_TIME, hello_time) || @@ -1087,19 +1086,16 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) nla_put_u32(skb, IFLA_BR_STP_STATE, stp_enabled) || nla_put_u16(skb, IFLA_BR_PRIORITY, priority) || nla_put_u8(skb, IFLA_BR_VLAN_FILTERING, vlan_enabled) || - nla_put_u16(skb, IFLA_BR_GROUP_FWD_MASK, group_fwd_mask) || - nla_put(skb, IFLA_BR_ROOT_ID, sizeof(root_id), &root_id) || - nla_put(skb, IFLA_BR_BRIDGE_ID, sizeof(bridge_id), &bridge_id) || + nla_put_u16(skb, IFLA_BR_GROUP_FWD_MASK, br->group_fwd_mask) || + nla_put(skb, IFLA_BR_BRIDGE_ID, sizeof(struct ifla_bridge_id), + &br->bridge_id) || + nla_put(skb, IFLA_BR_ROOT_ID, sizeof(struct ifla_bridge_id), + &br->designated_root) || nla_put_u16(skb, IFLA_BR_ROOT_PORT, br->root_port) || nla_put_u32(skb, IFLA_BR_ROOT_PATH_COST, br->root_path_cost) || nla_put_u8(skb, IFLA_BR_TOPOLOGY_CHANGE, br->topology_change) || nla_put_u8(skb, IFLA_BR_TOPOLOGY_CHANGE_DETECTED, br->topology_change_detected) || - nla_put_u64(skb, IFLA_BR_HELLO_TIMER, hello_timer) || - nla_put_u64(skb, IFLA_BR_TCN_TIMER, tcn_timer) || - nla_put_u64(skb, IFLA_BR_TOPOLOGY_CHANGE_TIMER, - topology_change_timer) || - nla_put_u64(skb, IFLA_BR_GC_TIMER, gc_timer) || nla_put(skb, IFLA_BR_GROUP_ADDR, ETH_ALEN, br->group_addr)) return -EMSGSIZE; -- cgit v0.10.2 From 00a93babd06aaad31d23384cda576ede0f586a8c Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Mon, 5 Oct 2015 13:09:46 +0200 Subject: openvswitch: add tunnel protocol to sw_flow_key Store tunnel protocol (AF_INET or AF_INET6) in sw_flow_key. This field now also acts as an indicator whether the flow contains tunnel data (this was previously indicated by tun_key.u.ipv4.dst being set but with IPv6 addresses in an union with IPv4 ones this won't work anymore). The new field was added to a hole in sw_flow_key. Signed-off-by: Jiri Benc Acked-by: Pravin B Shelar Acked-by: Thomas Graf Signed-off-by: David S. Miller diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index c8db44a..0ea128e 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c @@ -698,8 +698,7 @@ int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info, { /* Extract metadata from packet. */ if (tun_info) { - if (ip_tunnel_info_af(tun_info) != AF_INET) - return -EINVAL; + key->tun_proto = ip_tunnel_info_af(tun_info); memcpy(&key->tun_key, &tun_info->key, sizeof(key->tun_key)); if (tun_info->options_len) { @@ -714,6 +713,7 @@ int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info, key->tun_opts_len = 0; } } else { + key->tun_proto = 0; key->tun_opts_len = 0; memset(&key->tun_key, 0, sizeof(key->tun_key)); } diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h index fe527d2..5688e33 100644 --- a/net/openvswitch/flow.h +++ b/net/openvswitch/flow.h @@ -63,6 +63,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 tun_proto; /* Protocol of encapsulating tunnel. */ u32 ovs_flow_hash; /* Datapath computed hash value. */ u32 recirc_id; /* Recirculation ID. */ struct { diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index 5c030a4..6be701f6 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -643,6 +643,10 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr, } SW_FLOW_KEY_PUT(match, tun_key.tun_flags, tun_flags, is_mask); + if (is_mask) + SW_FLOW_KEY_MEMSET_FIELD(match, tun_proto, 0xff, true); + else + SW_FLOW_KEY_PUT(match, tun_proto, AF_INET, false); if (rem > 0) { OVS_NLERR(log, "IPv4 tunnel attribute has %d unknown bytes.", @@ -1194,7 +1198,7 @@ int ovs_nla_get_match(struct net *net, struct sw_flow_match *match, /* The userspace does not send tunnel attributes that * are 0, but we should not wildcard them nonetheless. */ - if (match->key->tun_key.u.ipv4.dst) + if (match->key->tun_proto) SW_FLOW_KEY_MEMSET_FIELD(match, tun_key, 0xff, true); @@ -1367,7 +1371,7 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey, if (nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, output->phy.priority)) goto nla_put_failure; - if ((swkey->tun_key.u.ipv4.dst || is_mask)) { + if ((swkey->tun_proto || is_mask)) { const void *opts = NULL; if (output->tun_key.tun_flags & TUNNEL_OPTIONS_PRESENT) @@ -1913,6 +1917,8 @@ static int validate_and_copy_set_tun(const struct nlattr *attr, tun_info = &tun_dst->u.tun_info; tun_info->mode = IP_TUNNEL_INFO_TX; + if (key.tun_proto == AF_INET6) + tun_info->mode |= IP_TUNNEL_INFO_IPV6; tun_info->key = key.tun_key; /* We need to store the options in the action itself since diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c index f2ea83b..95dbced 100644 --- a/net/openvswitch/flow_table.c +++ b/net/openvswitch/flow_table.c @@ -427,7 +427,7 @@ static u32 flow_hash(const struct sw_flow_key *key, static int flow_key_start(const struct sw_flow_key *key) { - if (key->tun_key.u.ipv4.dst) + if (key->tun_proto) return 0; else return rounddown(offsetof(struct sw_flow_key, phy), -- cgit v0.10.2 From 6b26ba3a7d952e611dcde1f3f77ce63bcc70540a Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Mon, 5 Oct 2015 13:09:47 +0200 Subject: openvswitch: netlink attributes for IPv6 tunneling Add netlink attributes for IPv6 tunnel addresses. This enables IPv6 support for tunnels. Signed-off-by: Jiri Benc Acked-by: Pravin B Shelar Acked-by: Thomas Graf Signed-off-by: David S. Miller diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index 32e07d8..4036e1b 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h @@ -349,6 +349,8 @@ enum ovs_tunnel_key_attr { OVS_TUNNEL_KEY_ATTR_TP_SRC, /* be16 src Transport Port. */ OVS_TUNNEL_KEY_ATTR_TP_DST, /* be16 dst Transport Port. */ OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS, /* Nested OVS_VXLAN_EXT_* */ + OVS_TUNNEL_KEY_ATTR_IPV6_SRC, /* struct in6_addr src IPv6 address. */ + OVS_TUNNEL_KEY_ATTR_IPV6_DST, /* struct in6_addr dst IPv6 address. */ __OVS_TUNNEL_KEY_ATTR_MAX }; diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index 6be701f6..77850f1 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -262,8 +262,8 @@ size_t ovs_tun_key_attr_size(void) * updating this function. */ return nla_total_size(8) /* OVS_TUNNEL_KEY_ATTR_ID */ - + nla_total_size(4) /* OVS_TUNNEL_KEY_ATTR_IPV4_SRC */ - + nla_total_size(4) /* OVS_TUNNEL_KEY_ATTR_IPV4_DST */ + + nla_total_size(16) /* OVS_TUNNEL_KEY_ATTR_IPV[46]_SRC */ + + nla_total_size(16) /* OVS_TUNNEL_KEY_ATTR_IPV[46]_DST */ + nla_total_size(1) /* OVS_TUNNEL_KEY_ATTR_TOS */ + nla_total_size(1) /* OVS_TUNNEL_KEY_ATTR_TTL */ + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT */ @@ -323,6 +323,8 @@ static const struct ovs_len_tbl ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1] [OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS] = { .len = OVS_ATTR_VARIABLE }, [OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS] = { .len = OVS_ATTR_NESTED, .next = ovs_vxlan_ext_key_lens }, + [OVS_TUNNEL_KEY_ATTR_IPV6_SRC] = { .len = sizeof(struct in6_addr) }, + [OVS_TUNNEL_KEY_ATTR_IPV6_DST] = { .len = sizeof(struct in6_addr) }, }; /* The size of the argument for each %OVS_KEY_ATTR_* Netlink attribute. */ @@ -542,14 +544,14 @@ static int vxlan_tun_opt_from_nlattr(const struct nlattr *attr, return 0; } -static int ipv4_tun_from_nlattr(const struct nlattr *attr, - struct sw_flow_match *match, bool is_mask, - bool log) +static int ip_tun_from_nlattr(const struct nlattr *attr, + struct sw_flow_match *match, bool is_mask, + bool log) { struct nlattr *a; int rem; bool ttl = false; - __be16 tun_flags = 0; + __be16 tun_flags = 0, ipv4 = false, ipv6 = false; int opts_type = 0; nla_for_each_nested(a, attr, rem) { @@ -578,10 +580,22 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr, case OVS_TUNNEL_KEY_ATTR_IPV4_SRC: SW_FLOW_KEY_PUT(match, tun_key.u.ipv4.src, nla_get_in_addr(a), is_mask); + ipv4 = true; break; case OVS_TUNNEL_KEY_ATTR_IPV4_DST: SW_FLOW_KEY_PUT(match, tun_key.u.ipv4.dst, nla_get_in_addr(a), is_mask); + ipv4 = true; + break; + case OVS_TUNNEL_KEY_ATTR_IPV6_SRC: + SW_FLOW_KEY_PUT(match, tun_key.u.ipv6.dst, + nla_get_in6_addr(a), is_mask); + ipv6 = true; + break; + case OVS_TUNNEL_KEY_ATTR_IPV6_DST: + SW_FLOW_KEY_PUT(match, tun_key.u.ipv6.dst, + nla_get_in6_addr(a), is_mask); + ipv6 = true; break; case OVS_TUNNEL_KEY_ATTR_TOS: SW_FLOW_KEY_PUT(match, tun_key.tos, @@ -636,7 +650,7 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr, opts_type = type; break; default: - OVS_NLERR(log, "Unknown IPv4 tunnel attribute %d", + OVS_NLERR(log, "Unknown IP tunnel attribute %d", type); return -EINVAL; } @@ -646,22 +660,36 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr, if (is_mask) SW_FLOW_KEY_MEMSET_FIELD(match, tun_proto, 0xff, true); else - SW_FLOW_KEY_PUT(match, tun_proto, AF_INET, false); + SW_FLOW_KEY_PUT(match, tun_proto, ipv6 ? AF_INET6 : AF_INET, + false); if (rem > 0) { - OVS_NLERR(log, "IPv4 tunnel attribute has %d unknown bytes.", + OVS_NLERR(log, "IP tunnel attribute has %d unknown bytes.", rem); return -EINVAL; } + if (ipv4 && ipv6) { + OVS_NLERR(log, "Mixed IPv4 and IPv6 tunnel attributes"); + return -EINVAL; + } + if (!is_mask) { - if (!match->key->tun_key.u.ipv4.dst) { + if (!ipv4 && !ipv6) { + OVS_NLERR(log, "IP tunnel dst address not specified"); + return -EINVAL; + } + if (ipv4 && !match->key->tun_key.u.ipv4.dst) { OVS_NLERR(log, "IPv4 tunnel dst address is zero"); return -EINVAL; } + if (ipv6 && ipv6_addr_any(&match->key->tun_key.u.ipv6.dst)) { + OVS_NLERR(log, "IPv6 tunnel dst address is zero"); + return -EINVAL; + } if (!ttl) { - OVS_NLERR(log, "IPv4 tunnel TTL not specified."); + OVS_NLERR(log, "IP tunnel TTL not specified."); return -EINVAL; } } @@ -686,21 +714,36 @@ static int vxlan_opt_to_nlattr(struct sk_buff *skb, return 0; } -static int __ipv4_tun_to_nlattr(struct sk_buff *skb, - const struct ip_tunnel_key *output, - const void *tun_opts, int swkey_tun_opts_len) +static int __ip_tun_to_nlattr(struct sk_buff *skb, + const struct ip_tunnel_key *output, + const void *tun_opts, int swkey_tun_opts_len, + unsigned short tun_proto) { if (output->tun_flags & TUNNEL_KEY && nla_put_be64(skb, OVS_TUNNEL_KEY_ATTR_ID, output->tun_id)) return -EMSGSIZE; - if (output->u.ipv4.src && - nla_put_in_addr(skb, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, - output->u.ipv4.src)) - return -EMSGSIZE; - if (output->u.ipv4.dst && - nla_put_in_addr(skb, OVS_TUNNEL_KEY_ATTR_IPV4_DST, - output->u.ipv4.dst)) - return -EMSGSIZE; + switch (tun_proto) { + case AF_INET: + if (output->u.ipv4.src && + nla_put_in_addr(skb, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, + output->u.ipv4.src)) + return -EMSGSIZE; + if (output->u.ipv4.dst && + nla_put_in_addr(skb, OVS_TUNNEL_KEY_ATTR_IPV4_DST, + output->u.ipv4.dst)) + return -EMSGSIZE; + break; + case AF_INET6: + if (!ipv6_addr_any(&output->u.ipv6.src) && + nla_put_in6_addr(skb, OVS_TUNNEL_KEY_ATTR_IPV6_SRC, + &output->u.ipv6.src)) + return -EMSGSIZE; + if (!ipv6_addr_any(&output->u.ipv6.dst) && + nla_put_in6_addr(skb, OVS_TUNNEL_KEY_ATTR_IPV6_DST, + &output->u.ipv6.dst)) + return -EMSGSIZE; + break; + } if (output->tos && nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TOS, output->tos)) return -EMSGSIZE; @@ -734,9 +777,10 @@ static int __ipv4_tun_to_nlattr(struct sk_buff *skb, return 0; } -static int ipv4_tun_to_nlattr(struct sk_buff *skb, - const struct ip_tunnel_key *output, - const void *tun_opts, int swkey_tun_opts_len) +static int ip_tun_to_nlattr(struct sk_buff *skb, + const struct ip_tunnel_key *output, + const void *tun_opts, int swkey_tun_opts_len, + unsigned short tun_proto) { struct nlattr *nla; int err; @@ -745,7 +789,8 @@ static int ipv4_tun_to_nlattr(struct sk_buff *skb, if (!nla) return -EMSGSIZE; - err = __ipv4_tun_to_nlattr(skb, output, tun_opts, swkey_tun_opts_len); + err = __ip_tun_to_nlattr(skb, output, tun_opts, swkey_tun_opts_len, + tun_proto); if (err) return err; @@ -757,9 +802,10 @@ int ovs_nla_put_egress_tunnel_key(struct sk_buff *skb, const struct ip_tunnel_info *egress_tun_info, const void *egress_tun_opts) { - return __ipv4_tun_to_nlattr(skb, &egress_tun_info->key, - egress_tun_opts, - egress_tun_info->options_len); + return __ip_tun_to_nlattr(skb, &egress_tun_info->key, + egress_tun_opts, + egress_tun_info->options_len, + ip_tunnel_info_af(egress_tun_info)); } static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match, @@ -810,8 +856,8 @@ static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match, *attrs &= ~(1 << OVS_KEY_ATTR_SKB_MARK); } if (*attrs & (1 << OVS_KEY_ATTR_TUNNEL)) { - if (ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], match, - is_mask, log) < 0) + if (ip_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], match, + is_mask, log) < 0) return -EINVAL; *attrs &= ~(1 << OVS_KEY_ATTR_TUNNEL); } @@ -1377,8 +1423,8 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey, if (output->tun_key.tun_flags & TUNNEL_OPTIONS_PRESENT) opts = TUN_METADATA_OPTS(output, swkey->tun_opts_len); - if (ipv4_tun_to_nlattr(skb, &output->tun_key, opts, - swkey->tun_opts_len)) + if (ip_tun_to_nlattr(skb, &output->tun_key, opts, + swkey->tun_opts_len, swkey->tun_proto)) goto nla_put_failure; } @@ -1881,7 +1927,7 @@ static int validate_and_copy_set_tun(const struct nlattr *attr, int err = 0, start, opts_type; ovs_match_init(&match, &key, NULL); - opts_type = ipv4_tun_from_nlattr(nla_data(attr), &match, false, log); + opts_type = ip_tun_from_nlattr(nla_data(attr), &match, false, log); if (opts_type < 0) return opts_type; @@ -2380,10 +2426,11 @@ static int set_action_to_attr(const struct nlattr *a, struct sk_buff *skb) if (!start) return -EMSGSIZE; - err = ipv4_tun_to_nlattr(skb, &tun_info->key, - tun_info->options_len ? + err = ip_tun_to_nlattr(skb, &tun_info->key, + tun_info->options_len ? ip_tunnel_info_opts(tun_info) : NULL, - tun_info->options_len); + tun_info->options_len, + ip_tunnel_info_af(tun_info)); if (err) return err; nla_nest_end(skb, start); -- cgit v0.10.2 From 66cebb86ac77c6dff0751efa382551cab24075cd Mon Sep 17 00:00:00 2001 From: Claudiu Manoil Date: Mon, 5 Oct 2015 17:19:57 +0300 Subject: doc: dt: net: Add fsl,wake-on-filer for eTSEC Add the "fsl,wake-on-filer" property for eTSEC nodes to indicate that the system has the power management infrastructure needed to be able to wake up the system via FGPI (filer, aka. h/w rx parser) interrupt. Cc: Li Yang Cc: Zhao Chenhui Signed-off-by: Claudiu Manoil Signed-off-by: David S. Miller diff --git a/Documentation/devicetree/bindings/net/fsl-tsec-phy.txt b/Documentation/devicetree/bindings/net/fsl-tsec-phy.txt index 1e97532..db74f0d 100644 --- a/Documentation/devicetree/bindings/net/fsl-tsec-phy.txt +++ b/Documentation/devicetree/bindings/net/fsl-tsec-phy.txt @@ -57,6 +57,10 @@ Properties: "rgmii-id", as all other connection types are detected by hardware. - fsl,magic-packet : If present, indicates that the hardware supports waking up via magic packet. + - fsl,wake-on-filer : If present, indicates that the hardware supports + waking up by Filer General Purpose Interrupt (FGPI) asserted on the + Rx int line. This is an advanced power management capability allowing + certain packet types (user) defined by filer rules to wake up the system. - bd-stash : If present, indicates that the hardware supports stashing buffer descriptors in the L2. - rx-stash-len : Denotes the number of bytes of a received buffer to stash -- cgit v0.10.2 From 70963d245ed11412658b89fe308b6188b62259c7 Mon Sep 17 00:00:00 2001 From: Claudiu Manoil Date: Mon, 5 Oct 2015 17:19:58 +0300 Subject: powerpc: dts: p1022si: Add fsl,wake-on-filer for eTSEC Enable the "wake-on-filer" (aka. wake on user defined packet) wake on lan capability for the eTSEC ethernet nodes. Cc: Li Yang Cc: Zhao Chenhui Signed-off-by: Claudiu Manoil Signed-off-by: David S. Miller diff --git a/arch/powerpc/boot/dts/fsl/p1022si-post.dtsi b/arch/powerpc/boot/dts/fsl/p1022si-post.dtsi index 426bf41..5f51b7b 100644 --- a/arch/powerpc/boot/dts/fsl/p1022si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p1022si-post.dtsi @@ -224,10 +224,12 @@ /include/ "pq3-etsec2-0.dtsi" enet0: enet0_grp2: ethernet@b0000 { + fsl,wake-on-filer; }; /include/ "pq3-etsec2-1.dtsi" enet1: enet1_grp2: ethernet@b1000 { + fsl,wake-on-filer; }; global-utilities@e0000 { -- cgit v0.10.2 From 3e905b80b92146d8dd0bf2da3ade0944a3026597 Mon Sep 17 00:00:00 2001 From: Claudiu Manoil Date: Mon, 5 Oct 2015 17:19:59 +0300 Subject: gianfar: Add WAKE_UCAST and "wake-on-filer" support This enables eTSEC's filer (Rx parser) and the FGPI Rx interrupt (Filer General Purpose Interrupt) as a wakeup source event. Upon entering suspend state, the eTSEC filer is given a rule to match incoming L2 unicast packets. A packet matching the rule will be enqueued in the Rx ring and a FGPI Rx interrupt will be asserted by the filer to wakeup the system. Other packet types will be dropped. On resume the filer table is restored to the content before entering suspend state. The set of rules from gfar_filer_config_wol() could be extended to implement other WoL capabilities as well. The "fsl,wake-on-filer" DT binding enables this capability on certain platforms that feature the necessary power management infrastructure, targeting mainly printing and imaging applications. (refer to Power Management section of the SoC Ref Man) Cc: Li Yang Cc: Zhao Chenhui Signed-off-by: Claudiu Manoil Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 710715f..7f5389c 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -907,6 +907,9 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev) if (of_find_property(np, "fsl,magic-packet", NULL)) priv->device_flags |= FSL_GIANFAR_DEV_HAS_MAGIC_PACKET; + if (of_get_property(np, "fsl,wake-on-filer", NULL)) + priv->device_flags |= FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER; + priv->phy_node = of_parse_phandle(np, "phy-handle", 0); /* In the case of a fixed PHY, the DT node associated @@ -1415,8 +1418,14 @@ static int gfar_probe(struct platform_device *ofdev) goto register_fail; } - device_set_wakeup_capable(&dev->dev, priv->device_flags & - FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); + if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) + priv->wol_supported |= GFAR_WOL_MAGIC; + + if ((priv->device_flags & FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER) && + priv->rx_filer_enable) + priv->wol_supported |= GFAR_WOL_FILER_UCAST; + + device_set_wakeup_capable(&ofdev->dev, priv->wol_supported); /* fill out IRQ number and name fields */ for (i = 0; i < priv->num_grps; i++) { @@ -1479,15 +1488,122 @@ static int gfar_remove(struct platform_device *ofdev) #ifdef CONFIG_PM +static void __gfar_filer_disable(struct gfar_private *priv) +{ + struct gfar __iomem *regs = priv->gfargrp[0].regs; + u32 temp; + + temp = gfar_read(®s->rctrl); + temp &= ~(RCTRL_FILREN | RCTRL_PRSDEP_INIT); + gfar_write(®s->rctrl, temp); +} + +static void __gfar_filer_enable(struct gfar_private *priv) +{ + struct gfar __iomem *regs = priv->gfargrp[0].regs; + u32 temp; + + temp = gfar_read(®s->rctrl); + temp |= RCTRL_FILREN | RCTRL_PRSDEP_INIT; + gfar_write(®s->rctrl, temp); +} + +/* Filer rules implementing wol capabilities */ +static void gfar_filer_config_wol(struct gfar_private *priv) +{ + unsigned int i; + u32 rqfcr; + + __gfar_filer_disable(priv); + + /* clear the filer table, reject any packet by default */ + rqfcr = RQFCR_RJE | RQFCR_CMP_MATCH; + for (i = 0; i <= MAX_FILER_IDX; i++) + gfar_write_filer(priv, i, rqfcr, 0); + + i = 0; + if (priv->wol_opts & GFAR_WOL_FILER_UCAST) { + /* unicast packet, accept it */ + struct net_device *ndev = priv->ndev; + /* get the default rx queue index */ + u8 qindex = (u8)priv->gfargrp[0].rx_queue->qindex; + u32 dest_mac_addr = (ndev->dev_addr[0] << 16) | + (ndev->dev_addr[1] << 8) | + ndev->dev_addr[2]; + + rqfcr = (qindex << 10) | RQFCR_AND | + RQFCR_CMP_EXACT | RQFCR_PID_DAH; + + gfar_write_filer(priv, i++, rqfcr, dest_mac_addr); + + dest_mac_addr = (ndev->dev_addr[3] << 16) | + (ndev->dev_addr[4] << 8) | + ndev->dev_addr[5]; + rqfcr = (qindex << 10) | RQFCR_GPI | + RQFCR_CMP_EXACT | RQFCR_PID_DAL; + gfar_write_filer(priv, i++, rqfcr, dest_mac_addr); + } + + __gfar_filer_enable(priv); +} + +static void gfar_filer_restore_table(struct gfar_private *priv) +{ + u32 rqfcr, rqfpr; + unsigned int i; + + __gfar_filer_disable(priv); + + for (i = 0; i <= MAX_FILER_IDX; i++) { + rqfcr = priv->ftp_rqfcr[i]; + rqfpr = priv->ftp_rqfpr[i]; + gfar_write_filer(priv, i, rqfcr, rqfpr); + } + + __gfar_filer_enable(priv); +} + +/* gfar_start() for Rx only and with the FGPI filer interrupt enabled */ +static void gfar_start_wol_filer(struct gfar_private *priv) +{ + struct gfar __iomem *regs = priv->gfargrp[0].regs; + u32 tempval; + int i = 0; + + /* Enable Rx hw queues */ + gfar_write(®s->rqueue, priv->rqueue); + + /* Initialize DMACTRL to have WWR and WOP */ + tempval = gfar_read(®s->dmactrl); + tempval |= DMACTRL_INIT_SETTINGS; + gfar_write(®s->dmactrl, tempval); + + /* Make sure we aren't stopped */ + tempval = gfar_read(®s->dmactrl); + tempval &= ~DMACTRL_GRS; + gfar_write(®s->dmactrl, tempval); + + for (i = 0; i < priv->num_grps; i++) { + regs = priv->gfargrp[i].regs; + /* Clear RHLT, so that the DMA starts polling now */ + gfar_write(®s->rstat, priv->gfargrp[i].rstat); + /* enable the Filer General Purpose Interrupt */ + gfar_write(®s->imask, IMASK_FGPI); + } + + /* Enable Rx DMA */ + tempval = gfar_read(®s->maccfg1); + tempval |= MACCFG1_RX_EN; + gfar_write(®s->maccfg1, tempval); +} + static int gfar_suspend(struct device *dev) { struct gfar_private *priv = dev_get_drvdata(dev); struct net_device *ndev = priv->ndev; struct gfar __iomem *regs = priv->gfargrp[0].regs; u32 tempval; - int magic_packet = priv->wol_en && - (priv->device_flags & - FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); + u16 wol = priv->wol_opts; if (!netif_running(ndev)) return 0; @@ -1499,7 +1615,7 @@ static int gfar_suspend(struct device *dev) gfar_halt(priv); - if (magic_packet) { + if (wol & GFAR_WOL_MAGIC) { /* Enable interrupt on Magic Packet */ gfar_write(®s->imask, IMASK_MAG); @@ -1513,6 +1629,10 @@ static int gfar_suspend(struct device *dev) tempval |= MACCFG1_RX_EN; gfar_write(®s->maccfg1, tempval); + } else if (wol & GFAR_WOL_FILER_UCAST) { + gfar_filer_config_wol(priv); + gfar_start_wol_filer(priv); + } else { phy_stop(priv->phydev); } @@ -1526,18 +1646,22 @@ static int gfar_resume(struct device *dev) struct net_device *ndev = priv->ndev; struct gfar __iomem *regs = priv->gfargrp[0].regs; u32 tempval; - int magic_packet = priv->wol_en && - (priv->device_flags & - FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); + u16 wol = priv->wol_opts; if (!netif_running(ndev)) return 0; - if (magic_packet) { + if (wol & GFAR_WOL_MAGIC) { /* Disable Magic Packet mode */ tempval = gfar_read(®s->maccfg2); tempval &= ~MACCFG2_MPEN; gfar_write(®s->maccfg2, tempval); + + } else if (wol & GFAR_WOL_FILER_UCAST) { + /* need to stop rx only, tx is already down */ + gfar_halt(priv); + gfar_filer_restore_table(priv); + } else { phy_start(priv->phydev); } @@ -1998,6 +2122,8 @@ static int register_grp_irqs(struct gfar_priv_grp *grp) gfar_irq(grp, RX)->irq); goto rx_irq_fail; } + enable_irq_wake(gfar_irq(grp, RX)->irq); + } else { err = request_irq(gfar_irq(grp, TX)->irq, gfar_interrupt, 0, gfar_irq(grp, TX)->name, grp); @@ -2743,7 +2869,14 @@ irqreturn_t gfar_receive(int irq, void *grp_id) { struct gfar_priv_grp *grp = (struct gfar_priv_grp *)grp_id; unsigned long flags; - u32 imask; + u32 imask, ievent; + + ievent = gfar_read(&grp->regs->ievent); + + if (unlikely(ievent & IEVENT_FGPI)) { + gfar_write(&grp->regs->ievent, IEVENT_FGPI); + return IRQ_HANDLED; + } if (likely(napi_schedule_prep(&grp->napi_rx))) { spin_lock_irqsave(&grp->grplock, flags); diff --git a/drivers/net/ethernet/freescale/gianfar.h b/drivers/net/ethernet/freescale/gianfar.h index 8c19948..f266b20 100644 --- a/drivers/net/ethernet/freescale/gianfar.h +++ b/drivers/net/ethernet/freescale/gianfar.h @@ -340,6 +340,7 @@ extern const char gfar_driver_version[]; #define IEVENT_MAG 0x00000800 #define IEVENT_GRSC 0x00000100 #define IEVENT_RXF0 0x00000080 +#define IEVENT_FGPI 0x00000010 #define IEVENT_FIR 0x00000008 #define IEVENT_FIQ 0x00000004 #define IEVENT_DPE 0x00000002 @@ -372,6 +373,7 @@ extern const char gfar_driver_version[]; #define IMASK_MAG 0x00000800 #define IMASK_GRSC 0x00000100 #define IMASK_RXFEN0 0x00000080 +#define IMASK_FGPI 0x00000010 #define IMASK_FIR 0x00000008 #define IMASK_FIQ 0x00000004 #define IMASK_DPE 0x00000002 @@ -540,6 +542,9 @@ extern const char gfar_driver_version[]; #define GFAR_INT_NAME_MAX (IFNAMSIZ + 6) /* '_g#_xx' */ +#define GFAR_WOL_MAGIC 0x00000001 +#define GFAR_WOL_FILER_UCAST 0x00000002 + struct txbd8 { union { @@ -917,6 +922,7 @@ struct gfar { #define FSL_GIANFAR_DEV_HAS_BD_STASHING 0x00000200 #define FSL_GIANFAR_DEV_HAS_BUF_STASHING 0x00000400 #define FSL_GIANFAR_DEV_HAS_TIMER 0x00000800 +#define FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER 0x00001000 #if (MAXGROUPS == 2) #define DEFAULT_MAPPING 0xAA @@ -1161,8 +1167,6 @@ struct gfar_private { extended_hash:1, bd_stash_en:1, rx_filer_enable:1, - /* Wake-on-LAN enabled */ - wol_en:1, /* Enable priorty based Tx scheduling in Hw */ prio_sched_en:1, /* Flow control flags */ @@ -1191,6 +1195,10 @@ struct gfar_private { u32 __iomem *hash_regs[16]; int hash_width; + /* wake-on-lan settings */ + u16 wol_opts; + u16 wol_supported; + /*Filer table*/ unsigned int ftp_rqfpr[MAX_FILER_IDX + 1]; unsigned int ftp_rqfcr[MAX_FILER_IDX + 1]; diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c index 6bdc891..fb7f8d6 100644 --- a/drivers/net/ethernet/freescale/gianfar_ethtool.c +++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c @@ -644,28 +644,49 @@ static void gfar_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct gfar_private *priv = netdev_priv(dev); - if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) { - wol->supported = WAKE_MAGIC; - wol->wolopts = priv->wol_en ? WAKE_MAGIC : 0; - } else { - wol->supported = wol->wolopts = 0; - } + wol->supported = 0; + wol->wolopts = 0; + + if (priv->wol_supported & GFAR_WOL_MAGIC) + wol->supported |= WAKE_MAGIC; + + if (priv->wol_supported & GFAR_WOL_FILER_UCAST) + wol->supported |= WAKE_UCAST; + + if (priv->wol_opts & GFAR_WOL_MAGIC) + wol->wolopts |= WAKE_MAGIC; + + if (priv->wol_opts & GFAR_WOL_FILER_UCAST) + wol->wolopts |= WAKE_UCAST; } static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct gfar_private *priv = netdev_priv(dev); + u16 wol_opts = 0; + int err; - if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) && - wol->wolopts != 0) + if (!priv->wol_supported && wol->wolopts) return -EINVAL; - if (wol->wolopts & ~WAKE_MAGIC) + if (wol->wolopts & ~(WAKE_MAGIC | WAKE_UCAST)) return -EINVAL; - device_set_wakeup_enable(&dev->dev, wol->wolopts & WAKE_MAGIC); + if (wol->wolopts & WAKE_MAGIC) { + wol_opts |= GFAR_WOL_MAGIC; + } else { + if (wol->wolopts & WAKE_UCAST) + wol_opts |= GFAR_WOL_FILER_UCAST; + } + + wol_opts &= priv->wol_supported; + priv->wol_opts = 0; + + err = device_set_wakeup_enable(priv->dev, wol_opts); + if (err) + return err; - priv->wol_en = !!device_may_wakeup(&dev->dev); + priv->wol_opts = wol_opts; return 0; } -- cgit v0.10.2 From 4148987a5111b0c8062bd78f39a67c361f621a39 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 5 Oct 2015 08:32:51 -0600 Subject: net: Fix vti use case with oif in dst lookups for IPv6 It occurred to me yesterday that 741a11d9e4103 ("net: ipv6: Add RT6_LOOKUP_F_IFACE flag if oif is set") means that xfrm6_dst_lookup needs the FLOWI_FLAG_SKIP_NH_OIF flag set. This latest commit causes the oif to be considered in lookups which is known to break vti. This explains why 58189ca7b274 did not the IPv6 change at the time it was submitted. Fixes: 42a7b32b73d6 ("xfrm: Add oif to dst lookups") Signed-off-by: David Ahern Signed-off-by: David S. Miller diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 69cee4e..08c9c93 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -37,6 +37,7 @@ static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, int oif, memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_oif = oif; + fl6.flowi6_flags = FLOWI_FLAG_SKIP_NH_OIF; memcpy(&fl6.daddr, daddr, sizeof(fl6.daddr)); if (saddr) memcpy(&fl6.saddr, saddr, sizeof(fl6.saddr)); -- cgit v0.10.2 From 6e2895a8e3824eb5611c97a015a3b6d678b4503e Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 5 Oct 2015 08:51:23 -0700 Subject: net: Rename FLOWI_FLAG_VRFSRC to FLOWI_FLAG_L3MDEV_SRC Signed-off-by: David Ahern Signed-off-by: David S. Miller diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 4743963..4fd5af1a 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -208,7 +208,7 @@ static netdev_tx_t vrf_process_v4_outbound(struct sk_buff *skb, .flowi4_oif = vrf_dev->ifindex, .flowi4_iif = LOOPBACK_IFINDEX, .flowi4_tos = RT_TOS(ip4h->tos), - .flowi4_flags = FLOWI_FLAG_ANYSRC | FLOWI_FLAG_VRFSRC | + .flowi4_flags = FLOWI_FLAG_ANYSRC | FLOWI_FLAG_L3MDEV_SRC | FLOWI_FLAG_SKIP_NH_OIF, .daddr = ip4h->daddr, }; @@ -545,7 +545,7 @@ static struct rtable *vrf_get_rtable(const struct net_device *dev, { struct rtable *rth = NULL; - if (!(fl4->flowi4_flags & FLOWI_FLAG_VRFSRC)) { + if (!(fl4->flowi4_flags & FLOWI_FLAG_L3MDEV_SRC)) { struct net_vrf *vrf = netdev_priv(dev); rth = vrf->rth; diff --git a/include/net/flow.h b/include/net/flow.h index 9b85db8..83969ee 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -34,7 +34,7 @@ struct flowi_common { __u8 flowic_flags; #define FLOWI_FLAG_ANYSRC 0x01 #define FLOWI_FLAG_KNOWN_NH 0x02 -#define FLOWI_FLAG_VRFSRC 0x04 +#define FLOWI_FLAG_L3MDEV_SRC 0x04 #define FLOWI_FLAG_SKIP_NH_OIF 0x08 __u32 flowic_secid; struct flowi_tunnel flowic_tun_key; diff --git a/include/net/route.h b/include/net/route.h index d32cb76..3e18d90 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -267,7 +267,7 @@ static inline void ip_route_connect_init(struct flowi4 *fl4, __be32 dst, __be32 flow_flags |= FLOWI_FLAG_ANYSRC; if (netif_index_is_l3_master(sock_net(sk), oif)) - flow_flags |= FLOWI_FLAG_VRFSRC | FLOWI_FLAG_SKIP_NH_OIF; + flow_flags |= FLOWI_FLAG_L3MDEV_SRC | FLOWI_FLAG_SKIP_NH_OIF; flowi4_init_output(fl4, oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, protocol, flow_flags, dst, src, dport, sport); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 156ba75..b2882cf 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1024,7 +1024,7 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) if (netif_index_is_l3_master(net, ipc.oif)) { flowi4_init_output(fl4, ipc.oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, sk->sk_protocol, - (flow_flags | FLOWI_FLAG_VRFSRC | + (flow_flags | FLOWI_FLAG_L3MDEV_SRC | FLOWI_FLAG_SKIP_NH_OIF), faddr, saddr, dport, inet->inet_sport); -- cgit v0.10.2 From fee6d4c777a125e56de9370db3b2bf359bf958d6 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 5 Oct 2015 08:51:24 -0700 Subject: net: Add netif_is_l3_slave IPv6 addrconf keys off of IFF_SLAVE so can not use it for L3 slave. Add a new private flag and add netif_is_l3_slave function for checking it. Signed-off-by: David Ahern Signed-off-by: David S. Miller diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 4fd5af1a..8713317 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -39,8 +39,6 @@ #define DRV_NAME "vrf" #define DRV_VERSION "1.0" -#define vrf_is_slave(dev) ((dev)->flags & IFF_SLAVE) - #define vrf_master_get_rcu(dev) \ ((struct net_device *)rcu_dereference(dev->rx_handler_data)) @@ -433,7 +431,7 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev) if (ret < 0) goto out_unregister; - port_dev->flags |= IFF_SLAVE; + port_dev->priv_flags |= IFF_L3MDEV_SLAVE; __vrf_insert_slave(queue, slave); cycle_netdev(port_dev); @@ -448,7 +446,7 @@ out_fail: static int vrf_add_slave(struct net_device *dev, struct net_device *port_dev) { - if (netif_is_l3_master(port_dev) || vrf_is_slave(port_dev)) + if (netif_is_l3_master(port_dev) || netif_is_l3_slave(port_dev)) return -EINVAL; return do_vrf_add_slave(dev, port_dev); @@ -462,7 +460,7 @@ static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev) struct slave *slave; netdev_upper_dev_unlink(port_dev, dev); - port_dev->flags &= ~IFF_SLAVE; + port_dev->priv_flags &= ~IFF_L3MDEV_SLAVE; netdev_rx_handler_unregister(port_dev); @@ -672,7 +670,7 @@ static int vrf_device_event(struct notifier_block *unused, if (event == NETDEV_UNREGISTER) { struct net_device *vrf_dev; - if (!vrf_is_slave(dev) || netif_is_l3_master(dev)) + if (!netif_is_l3_slave(dev)) goto out; vrf_dev = netdev_master_upper_dev_get(dev); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index b945078..b337440 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1261,6 +1261,7 @@ struct net_device_ops { * @IFF_L3MDEV_MASTER: device is an L3 master device * @IFF_NO_QUEUE: device can run without qdisc attached * @IFF_OPENVSWITCH: device is a Open vSwitch master + * @IFF_L3MDEV_SLAVE: device is enslaved to an L3 master device */ enum netdev_priv_flags { IFF_802_1Q_VLAN = 1<<0, @@ -1286,6 +1287,7 @@ enum netdev_priv_flags { IFF_L3MDEV_MASTER = 1<<20, IFF_NO_QUEUE = 1<<21, IFF_OPENVSWITCH = 1<<22, + IFF_L3MDEV_SLAVE = 1<<23, }; #define IFF_802_1Q_VLAN IFF_802_1Q_VLAN @@ -3830,6 +3832,11 @@ static inline bool netif_is_l3_master(const struct net_device *dev) return dev->priv_flags & IFF_L3MDEV_MASTER; } +static inline bool netif_is_l3_slave(const struct net_device *dev) +{ + return dev->priv_flags & IFF_L3MDEV_SLAVE; +} + static inline bool netif_is_bridge_master(const struct net_device *dev) { return dev->priv_flags & IFF_EBRIDGE; diff --git a/net/l3mdev/l3mdev.c b/net/l3mdev/l3mdev.c index ddf75ad..8e5ead3 100644 --- a/net/l3mdev/l3mdev.c +++ b/net/l3mdev/l3mdev.c @@ -26,11 +26,11 @@ int l3mdev_master_ifindex_rcu(struct net_device *dev) if (netif_is_l3_master(dev)) { ifindex = dev->ifindex; - } else if (dev->flags & IFF_SLAVE) { + } else if (netif_is_l3_slave(dev)) { struct net_device *master; master = netdev_master_upper_dev_get_rcu(dev); - if (master && netif_is_l3_master(master)) + if (master) ifindex = master->ifindex; } @@ -54,7 +54,7 @@ u32 l3mdev_fib_table_rcu(const struct net_device *dev) if (netif_is_l3_master(dev)) { if (dev->l3mdev_ops->l3mdev_fib_table) tb_id = dev->l3mdev_ops->l3mdev_fib_table(dev); - } else if (dev->flags & IFF_SLAVE) { + } else if (netif_is_l3_slave(dev)) { /* Users of netdev_master_upper_dev_get_rcu need non-const, * but current inet_*type functions take a const */ @@ -62,7 +62,7 @@ u32 l3mdev_fib_table_rcu(const struct net_device *dev) const struct net_device *master; master = netdev_master_upper_dev_get_rcu(_dev); - if (master && netif_is_l3_master(master) && + if (master && master->l3mdev_ops->l3mdev_fib_table) tb_id = master->l3mdev_ops->l3mdev_fib_table(master); } -- cgit v0.10.2 From 3ce58d84358c7b477811b5100152fad848f936fc Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 5 Oct 2015 08:51:25 -0700 Subject: net: Refactor path selection in __ip_route_output_key_hash VRF device needs the same path selection following lookup to set source address. Rather than duplicating code, move existing code into a function that is exported to modules. Code move only; no functional change. Signed-off-by: David Ahern Signed-off-by: David S. Miller diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 7a51fd8..ac5c6e8 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -329,6 +329,8 @@ static inline int fib_multipath_hash(__be32 saddr, __be32 daddr) } void fib_select_multipath(struct fib_result *res, int hash); +void fib_select_path(struct net *net, struct fib_result *res, + struct flowi4 *fl4, int mp_hash); /* Exported by fib_trie.c */ void fib_trie_init(void); diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 7bd698c..af77298 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -1557,3 +1557,24 @@ void fib_select_multipath(struct fib_result *res, int hash) res->nh_sel = 0; } #endif + +void fib_select_path(struct net *net, struct fib_result *res, + struct flowi4 *fl4, int mp_hash) +{ +#ifdef CONFIG_IP_ROUTE_MULTIPATH + if (res->fi->fib_nhs > 1 && fl4->flowi4_oif == 0) { + if (mp_hash < 0) + mp_hash = fib_multipath_hash(fl4->saddr, fl4->daddr); + fib_select_multipath(res, mp_hash); + } + else +#endif + if (!res->prefixlen && + res->table->tb_num_default > 1 && + res->type == RTN_UNICAST && !fl4->flowi4_oif) + fib_select_default(fl4, res); + + if (!fl4->saddr) + fl4->saddr = FIB_RES_PREFSRC(net, *res); +} +EXPORT_SYMBOL_GPL(fib_select_path); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 54297d3..54e6f45 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2238,21 +2238,7 @@ struct rtable *__ip_route_output_key_hash(struct net *net, struct flowi4 *fl4, goto make_route; } -#ifdef CONFIG_IP_ROUTE_MULTIPATH - if (res.fi->fib_nhs > 1 && fl4->flowi4_oif == 0) { - if (mp_hash < 0) - mp_hash = fib_multipath_hash(fl4->saddr, fl4->daddr); - fib_select_multipath(&res, mp_hash); - } - else -#endif - if (!res.prefixlen && - res.table->tb_num_default > 1 && - res.type == RTN_UNICAST && !fl4->flowi4_oif) - fib_select_default(fl4, &res); - - if (!fl4->saddr) - fl4->saddr = FIB_RES_PREFSRC(net, res); + fib_select_path(net, &res, fl4, mp_hash); dev_out = FIB_RES_DEV(res); fl4->flowi4_oif = dev_out->ifindex; -- cgit v0.10.2 From 8cbb512c923d5f695ff6265b2b741b1718e3b444 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 5 Oct 2015 08:51:26 -0700 Subject: net: Add source address lookup op for VRF Add operation to l3mdev to lookup source address for a given flow. Add support for the operation to VRF driver and convert existing IPv4 hooks to use the new lookup. Signed-off-by: David Ahern Signed-off-by: David S. Miller diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 8713317..6449976 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -36,6 +36,9 @@ #include #include +#define RT_FL_TOS(oldflp4) \ + ((oldflp4)->flowi4_tos & (IPTOS_RT_MASK | RTO_ONLINK)) + #define DRV_NAME "vrf" #define DRV_VERSION "1.0" @@ -553,9 +556,41 @@ static struct rtable *vrf_get_rtable(const struct net_device *dev, return rth; } +/* called under rcu_read_lock */ +static void vrf_get_saddr(struct net_device *dev, struct flowi4 *fl4) +{ + struct fib_result res = { .tclassid = 0 }; + struct net *net = dev_net(dev); + u32 orig_tos = fl4->flowi4_tos; + u8 flags = fl4->flowi4_flags; + u8 scope = fl4->flowi4_scope; + u8 tos = RT_FL_TOS(fl4); + + if (unlikely(!fl4->daddr)) + return; + + fl4->flowi4_flags |= FLOWI_FLAG_SKIP_NH_OIF; + fl4->flowi4_iif = LOOPBACK_IFINDEX; + fl4->flowi4_tos = tos & IPTOS_RT_MASK; + fl4->flowi4_scope = ((tos & RTO_ONLINK) ? + RT_SCOPE_LINK : RT_SCOPE_UNIVERSE); + + if (!fib_lookup(net, fl4, &res, 0)) { + if (res.type == RTN_LOCAL) + fl4->saddr = res.fi->fib_prefsrc ? : fl4->daddr; + else + fib_select_path(net, &res, fl4, -1); + } + + fl4->flowi4_flags = flags; + fl4->flowi4_tos = orig_tos; + fl4->flowi4_scope = scope; +} + static const struct l3mdev_ops vrf_l3mdev_ops = { .l3mdev_fib_table = vrf_fib_table, .l3mdev_get_rtable = vrf_get_rtable, + .l3mdev_get_saddr = vrf_get_saddr, }; static void vrf_get_drvinfo(struct net_device *dev, diff --git a/include/net/l3mdev.h b/include/net/l3mdev.h index 87cee05..44a19a1 100644 --- a/include/net/l3mdev.h +++ b/include/net/l3mdev.h @@ -17,12 +17,16 @@ * @l3mdev_fib_table: Get FIB table id to use for lookups * * @l3mdev_get_rtable: Get cached IPv4 rtable (dst_entry) for device + * + * @l3mdev_get_saddr: Get source address for a flow */ struct l3mdev_ops { u32 (*l3mdev_fib_table)(const struct net_device *dev); struct rtable * (*l3mdev_get_rtable)(const struct net_device *dev, const struct flowi4 *fl4); + void (*l3mdev_get_saddr)(struct net_device *dev, + struct flowi4 *fl4); }; #ifdef CONFIG_NET_L3_MASTER_DEV @@ -100,6 +104,25 @@ static inline bool netif_index_is_l3_master(struct net *net, int ifindex) return rc; } +static inline void l3mdev_get_saddr(struct net *net, int ifindex, + struct flowi4 *fl4) +{ + struct net_device *dev; + + if (ifindex) { + + rcu_read_lock(); + + dev = dev_get_by_index_rcu(net, ifindex); + if (dev && netif_is_l3_master(dev) && + dev->l3mdev_ops->l3mdev_get_saddr) { + dev->l3mdev_ops->l3mdev_get_saddr(dev, fl4); + } + + rcu_read_unlock(); + } +} + #else static inline int l3mdev_master_ifindex_rcu(struct net_device *dev) @@ -144,6 +167,10 @@ static inline bool netif_index_is_l3_master(struct net *net, int ifindex) return false; } +static inline void l3mdev_get_saddr(struct net *net, int ifindex, + struct flowi4 *fl4) +{ +} #endif #endif /* _NET_L3MDEV_H_ */ diff --git a/include/net/route.h b/include/net/route.h index 3e18d90..ee81307 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -266,9 +266,6 @@ static inline void ip_route_connect_init(struct flowi4 *fl4, __be32 dst, __be32 if (inet_sk(sk)->transparent) flow_flags |= FLOWI_FLAG_ANYSRC; - if (netif_index_is_l3_master(sock_net(sk), oif)) - flow_flags |= FLOWI_FLAG_L3MDEV_SRC | FLOWI_FLAG_SKIP_NH_OIF; - flowi4_init_output(fl4, oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, protocol, flow_flags, dst, src, dport, sport); } @@ -285,6 +282,10 @@ static inline struct rtable *ip_route_connect(struct flowi4 *fl4, ip_route_connect_init(fl4, dst, src, tos, oif, protocol, sport, dport, sk); + if (!src && oif) { + l3mdev_get_saddr(net, oif, fl4); + src = fl4->saddr; + } if (!dst || !src) { rt = __ip_route_output_key(net, fl4); if (IS_ERR(rt)) diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index b2882cf..e1fc129 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1017,30 +1017,14 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) fl4 = &fl4_stack; - /* unconnected socket. If output device is enslaved to a VRF - * device lookup source address from VRF table. This mimics - * behavior of ip_route_connect{_init}. - */ - if (netif_index_is_l3_master(net, ipc.oif)) { - flowi4_init_output(fl4, ipc.oif, sk->sk_mark, tos, - RT_SCOPE_UNIVERSE, sk->sk_protocol, - (flow_flags | FLOWI_FLAG_L3MDEV_SRC | - FLOWI_FLAG_SKIP_NH_OIF), - faddr, saddr, dport, - inet->inet_sport); - - rt = ip_route_output_flow(net, fl4, sk); - if (!IS_ERR(rt)) { - saddr = fl4->saddr; - ip_rt_put(rt); - } - } - flowi4_init_output(fl4, ipc.oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, sk->sk_protocol, flow_flags, faddr, saddr, dport, inet->inet_sport); + if (!saddr && ipc.oif) + l3mdev_get_saddr(net, ipc.oif, fl4); + security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); rt = ip_route_output_flow(net, fl4, sk); if (IS_ERR(rt)) { -- cgit v0.10.2 From bb191c3e874650ae8f701885f3dd5f8ea8989b19 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 5 Oct 2015 08:51:27 -0700 Subject: net: Add l3mdev saddr lookup to raw_sendmsg ping originated on box through a VRF device is showing up in tcpdump without a source address: $ tcpdump -n -i vrf-blue 08:58:33.311303 IP 0.0.0.0 > 10.2.2.254: ICMP echo request, id 2834, seq 1, length 64 08:58:33.311562 IP 10.2.2.254 > 10.2.2.2: ICMP echo reply, id 2834, seq 1, length 64 Add the call to l3mdev_get_saddr to raw_sendmsg. Signed-off-by: David Ahern Signed-off-by: David S. Miller diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 28ef8a9..09a07e8 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -484,6 +484,7 @@ static int raw_getfrag(void *from, char *to, int offset, int len, int odd, static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) { struct inet_sock *inet = inet_sk(sk); + struct net *net = sock_net(sk); struct ipcm_cookie ipc; struct rtable *rt = NULL; struct flowi4 fl4; @@ -543,7 +544,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ipc.oif = sk->sk_bound_dev_if; if (msg->msg_controllen) { - err = ip_cmsg_send(sock_net(sk), msg, &ipc, false); + err = ip_cmsg_send(net, msg, &ipc, false); if (err) goto out; if (ipc.opt) @@ -598,6 +599,9 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) (inet->hdrincl ? FLOWI_FLAG_KNOWN_NH : 0), daddr, saddr, 0, 0); + if (!saddr && ipc.oif) + l3mdev_get_saddr(net, ipc.oif, &fl4); + if (!inet->hdrincl) { rfv.msg = msg; rfv.hlen = 0; @@ -608,7 +612,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) } security_sk_classify_flow(sk, flowi4_to_flowi(&fl4)); - rt = ip_route_output_flow(sock_net(sk), &fl4, sk); + rt = ip_route_output_flow(net, &fl4, sk); if (IS_ERR(rt)) { err = PTR_ERR(rt); rt = NULL; -- cgit v0.10.2 From deaa0a6a930edc79081268bf23b196d0340499af Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 5 Oct 2015 10:49:04 -0700 Subject: net: Lookup actual route when oif is VRF device If the user specifies a VRF device in a get route query the custom route pointing to the VRF device is returned: $ ip route ls table vrf-red unreachable default broadcast 10.2.1.0 dev eth1 proto kernel scope link src 10.2.1.2 10.2.1.0/24 dev eth1 proto kernel scope link src 10.2.1.2 local 10.2.1.2 dev eth1 proto kernel scope host src 10.2.1.2 broadcast 10.2.1.255 dev eth1 proto kernel scope link src 10.2.1.2 $ ip route get oif vrf-red 10.2.1.40 10.2.1.40 dev vrf-red cache Add the flags to skip the custom route and go directly to the FIB. With this patch the actual route is returned: $ ip route get oif vrf-red 10.2.1.40 10.2.1.40 dev eth1 src 10.2.1.2 cache Signed-off-by: David Ahern Signed-off-by: David S. Miller diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 54e6f45..bf1486b 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2507,6 +2507,9 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) fl4.flowi4_oif = tb[RTA_OIF] ? nla_get_u32(tb[RTA_OIF]) : 0; fl4.flowi4_mark = mark; + if (netif_index_is_l3_master(net, fl4.flowi4_oif)) + fl4.flowi4_flags = FLOWI_FLAG_L3MDEV_SRC | FLOWI_FLAG_SKIP_NH_OIF; + if (iif) { struct net_device *dev; -- cgit v0.10.2 From 4ebc7660ab4559cad10b6595e05f70562bb26dc5 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Tue, 6 Oct 2015 14:11:55 +0200 Subject: bridge: netlink: export port's root id Add IFLA_BRPORT_ROOT_ID to allow getting the designated root id via netlink. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index c5b15bf..c45c1d7 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -292,6 +292,7 @@ enum { IFLA_BRPORT_PROXYARP, /* proxy ARP */ IFLA_BRPORT_LEARNING_SYNC, /* mac learning sync from device */ IFLA_BRPORT_PROXYARP_WIFI, /* proxy ARP for Wi-Fi */ + IFLA_BRPORT_ROOT_ID, /* designated root */ __IFLA_BRPORT_MAX }; #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 330abf4..cad4050 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -127,6 +127,7 @@ static inline size_t br_port_info_size(void) + nla_total_size(1) /* IFLA_BRPORT_UNICAST_FLOOD */ + nla_total_size(1) /* IFLA_BRPORT_PROXYARP */ + nla_total_size(1) /* IFLA_BRPORT_PROXYARP_WIFI */ + + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_ROOT_ID */ + 0; } @@ -160,7 +161,9 @@ static int br_port_fill_attrs(struct sk_buff *skb, nla_put_u8(skb, IFLA_BRPORT_UNICAST_FLOOD, !!(p->flags & BR_FLOOD)) || nla_put_u8(skb, IFLA_BRPORT_PROXYARP, !!(p->flags & BR_PROXYARP)) || nla_put_u8(skb, IFLA_BRPORT_PROXYARP_WIFI, - !!(p->flags & BR_PROXYARP_WIFI))) + !!(p->flags & BR_PROXYARP_WIFI)) || + nla_put(skb, IFLA_BRPORT_ROOT_ID, sizeof(struct ifla_bridge_id), + &p->designated_root)) return -EMSGSIZE; return 0; -- cgit v0.10.2 From 80df9a2692edf7afffda9282e716e7b1df198e07 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Tue, 6 Oct 2015 14:11:56 +0200 Subject: bridge: netlink: export port's bridge id Add IFLA_BRPORT_BRIDGE_ID to allow getting the designated bridge id via netlink. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index c45c1d7..e891c32 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -293,6 +293,7 @@ enum { IFLA_BRPORT_LEARNING_SYNC, /* mac learning sync from device */ IFLA_BRPORT_PROXYARP_WIFI, /* proxy ARP for Wi-Fi */ IFLA_BRPORT_ROOT_ID, /* designated root */ + IFLA_BRPORT_BRIDGE_ID, /* designated bridge */ __IFLA_BRPORT_MAX }; #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index cad4050..c3e0b73 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -128,6 +128,7 @@ static inline size_t br_port_info_size(void) + nla_total_size(1) /* IFLA_BRPORT_PROXYARP */ + nla_total_size(1) /* IFLA_BRPORT_PROXYARP_WIFI */ + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_ROOT_ID */ + + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_BRIDGE_ID */ + 0; } @@ -163,7 +164,9 @@ static int br_port_fill_attrs(struct sk_buff *skb, nla_put_u8(skb, IFLA_BRPORT_PROXYARP_WIFI, !!(p->flags & BR_PROXYARP_WIFI)) || nla_put(skb, IFLA_BRPORT_ROOT_ID, sizeof(struct ifla_bridge_id), - &p->designated_root)) + &p->designated_root) || + nla_put(skb, IFLA_BRPORT_BRIDGE_ID, sizeof(struct ifla_bridge_id), + &p->designated_bridge)) return -EMSGSIZE; return 0; -- cgit v0.10.2 From 96f94e7f4a216282a24819968184c881e6343692 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Tue, 6 Oct 2015 14:11:57 +0200 Subject: bridge: netlink: export port's designated cost and port Add IFLA_BRPORT_DESIGNATED_(COST|PORT) to allow getting the port's designated cost and port respectively via netlink. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index e891c32..837caf9 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -294,6 +294,8 @@ enum { IFLA_BRPORT_PROXYARP_WIFI, /* proxy ARP for Wi-Fi */ IFLA_BRPORT_ROOT_ID, /* designated root */ IFLA_BRPORT_BRIDGE_ID, /* designated bridge */ + IFLA_BRPORT_DESIGNATED_PORT, + IFLA_BRPORT_DESIGNATED_COST, __IFLA_BRPORT_MAX }; #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index c3e0b73..678d227 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -129,6 +129,8 @@ static inline size_t br_port_info_size(void) + nla_total_size(1) /* IFLA_BRPORT_PROXYARP_WIFI */ + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_ROOT_ID */ + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_BRIDGE_ID */ + + nla_total_size(sizeof(u16)) /* IFLA_BRPORT_DESIGNATED_PORT */ + + nla_total_size(sizeof(u16)) /* IFLA_BRPORT_DESIGNATED_COST */ + 0; } @@ -166,7 +168,9 @@ static int br_port_fill_attrs(struct sk_buff *skb, nla_put(skb, IFLA_BRPORT_ROOT_ID, sizeof(struct ifla_bridge_id), &p->designated_root) || nla_put(skb, IFLA_BRPORT_BRIDGE_ID, sizeof(struct ifla_bridge_id), - &p->designated_bridge)) + &p->designated_bridge) || + nla_put_u16(skb, IFLA_BRPORT_DESIGNATED_PORT, p->designated_port) || + nla_put_u16(skb, IFLA_BRPORT_DESIGNATED_COST, p->designated_cost)) return -EMSGSIZE; return 0; -- cgit v0.10.2 From 42d452c4b5e7bf0e3024fa9512ec462f70545ae5 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Tue, 6 Oct 2015 14:11:58 +0200 Subject: bridge: netlink: export port's id and number Add IFLA_BRPORT_(ID|NO) to allow getting port's port_id and port_no respectively via netlink. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 837caf9..6857563 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -296,6 +296,8 @@ enum { IFLA_BRPORT_BRIDGE_ID, /* designated bridge */ IFLA_BRPORT_DESIGNATED_PORT, IFLA_BRPORT_DESIGNATED_COST, + IFLA_BRPORT_ID, + IFLA_BRPORT_NO, __IFLA_BRPORT_MAX }; #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 678d227..e513327 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -131,6 +131,8 @@ static inline size_t br_port_info_size(void) + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_BRIDGE_ID */ + nla_total_size(sizeof(u16)) /* IFLA_BRPORT_DESIGNATED_PORT */ + nla_total_size(sizeof(u16)) /* IFLA_BRPORT_DESIGNATED_COST */ + + nla_total_size(sizeof(u16)) /* IFLA_BRPORT_ID */ + + nla_total_size(sizeof(u16)) /* IFLA_BRPORT_NO */ + 0; } @@ -170,7 +172,9 @@ static int br_port_fill_attrs(struct sk_buff *skb, nla_put(skb, IFLA_BRPORT_BRIDGE_ID, sizeof(struct ifla_bridge_id), &p->designated_bridge) || nla_put_u16(skb, IFLA_BRPORT_DESIGNATED_PORT, p->designated_port) || - nla_put_u16(skb, IFLA_BRPORT_DESIGNATED_COST, p->designated_cost)) + nla_put_u16(skb, IFLA_BRPORT_DESIGNATED_COST, p->designated_cost) || + nla_put_u16(skb, IFLA_BRPORT_ID, p->port_id) || + nla_put_u16(skb, IFLA_BRPORT_NO, p->port_no)) return -EMSGSIZE; return 0; -- cgit v0.10.2 From e08e838ac5707cb1f1294e0d53b31997a0367b99 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Tue, 6 Oct 2015 14:11:59 +0200 Subject: bridge: netlink: export port's topology_change_ack and config_pending Add IFLA_BRPORT_TOPOLOGY_CHANGE_ACK and IFLA_BRPORT_CONFIG_PENDING to allow getting port's topology_change_ack and config_pending respectively via netlink. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 6857563..1d6aeb1 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -298,6 +298,8 @@ enum { IFLA_BRPORT_DESIGNATED_COST, IFLA_BRPORT_ID, IFLA_BRPORT_NO, + IFLA_BRPORT_TOPOLOGY_CHANGE_ACK, + IFLA_BRPORT_CONFIG_PENDING, __IFLA_BRPORT_MAX }; #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index e513327..433d632 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -133,6 +133,8 @@ static inline size_t br_port_info_size(void) + nla_total_size(sizeof(u16)) /* IFLA_BRPORT_DESIGNATED_COST */ + nla_total_size(sizeof(u16)) /* IFLA_BRPORT_ID */ + nla_total_size(sizeof(u16)) /* IFLA_BRPORT_NO */ + + nla_total_size(sizeof(u8)) /* IFLA_BRPORT_TOPOLOGY_CHANGE_ACK */ + + nla_total_size(sizeof(u8)) /* IFLA_BRPORT_CONFIG_PENDING */ + 0; } @@ -174,7 +176,10 @@ static int br_port_fill_attrs(struct sk_buff *skb, nla_put_u16(skb, IFLA_BRPORT_DESIGNATED_PORT, p->designated_port) || nla_put_u16(skb, IFLA_BRPORT_DESIGNATED_COST, p->designated_cost) || nla_put_u16(skb, IFLA_BRPORT_ID, p->port_id) || - nla_put_u16(skb, IFLA_BRPORT_NO, p->port_no)) + nla_put_u16(skb, IFLA_BRPORT_NO, p->port_no) || + nla_put_u8(skb, IFLA_BRPORT_TOPOLOGY_CHANGE_ACK, + p->topology_change_ack) || + nla_put_u8(skb, IFLA_BRPORT_CONFIG_PENDING, p->config_pending)) return -EMSGSIZE; return 0; -- cgit v0.10.2 From 61c0a9a83e0b12c712cd686172446aba8ea48685 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Tue, 6 Oct 2015 14:12:00 +0200 Subject: bridge: netlink: export port's timer values Add the following attributes in order to export port's timer values: IFLA_BRPORT_MESSAGE_AGE_TIMER, IFLA_BRPORT_FORWARD_DELAY_TIMER and IFLA_BRPORT_HOLD_TIMER. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 1d6aeb1..9c17f60 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -300,6 +300,9 @@ enum { IFLA_BRPORT_NO, IFLA_BRPORT_TOPOLOGY_CHANGE_ACK, IFLA_BRPORT_CONFIG_PENDING, + IFLA_BRPORT_MESSAGE_AGE_TIMER, + IFLA_BRPORT_FORWARD_DELAY_TIMER, + IFLA_BRPORT_HOLD_TIMER, __IFLA_BRPORT_MAX }; #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 433d632..04b0e50 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -135,6 +135,9 @@ static inline size_t br_port_info_size(void) + nla_total_size(sizeof(u16)) /* IFLA_BRPORT_NO */ + nla_total_size(sizeof(u8)) /* IFLA_BRPORT_TOPOLOGY_CHANGE_ACK */ + nla_total_size(sizeof(u8)) /* IFLA_BRPORT_CONFIG_PENDING */ + + nla_total_size(sizeof(u64)) /* IFLA_BRPORT_MESSAGE_AGE_TIMER */ + + nla_total_size(sizeof(u64)) /* IFLA_BRPORT_FORWARD_DELAY_TIMER */ + + nla_total_size(sizeof(u64)) /* IFLA_BRPORT_HOLD_TIMER */ + 0; } @@ -156,6 +159,7 @@ static int br_port_fill_attrs(struct sk_buff *skb, const struct net_bridge_port *p) { u8 mode = !!(p->flags & BR_HAIRPIN_MODE); + u64 timerval; if (nla_put_u8(skb, IFLA_BRPORT_STATE, p->state) || nla_put_u16(skb, IFLA_BRPORT_PRIORITY, p->priority) || @@ -182,6 +186,16 @@ static int br_port_fill_attrs(struct sk_buff *skb, nla_put_u8(skb, IFLA_BRPORT_CONFIG_PENDING, p->config_pending)) return -EMSGSIZE; + timerval = br_timer_value(&p->message_age_timer); + if (nla_put_u64(skb, IFLA_BRPORT_MESSAGE_AGE_TIMER, timerval)) + return -EMSGSIZE; + timerval = br_timer_value(&p->forward_delay_timer); + if (nla_put_u64(skb, IFLA_BRPORT_FORWARD_DELAY_TIMER, timerval)) + return -EMSGSIZE; + timerval = br_timer_value(&p->hold_timer); + if (nla_put_u64(skb, IFLA_BRPORT_HOLD_TIMER, timerval)) + return -EMSGSIZE; + return 0; } -- cgit v0.10.2 From 9b0c6e4deb3df91bf0aea8158ea77dc58c9d90b6 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Tue, 6 Oct 2015 14:12:01 +0200 Subject: bridge: netlink: allow to flush port's fdb Add IFLA_BRPORT_FLUSH to allow flushing port's fdb similar to sysfs's flush. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 9c17f60..fcea39a 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -303,6 +303,7 @@ enum { IFLA_BRPORT_MESSAGE_AGE_TIMER, IFLA_BRPORT_FORWARD_DELAY_TIMER, IFLA_BRPORT_HOLD_TIMER, + IFLA_BRPORT_FLUSH, __IFLA_BRPORT_MAX }; #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 04b0e50..6468166 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -631,6 +631,9 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) return err; } + if (tb[IFLA_BRPORT_FLUSH]) + br_fdb_delete_by_port(p->br, p, 0, 0); + br_port_flags_change(p, old_flags ^ p->flags); return 0; } -- cgit v0.10.2 From 5d6ae479ab7ddf77bb22bdf739268581453ff886 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Tue, 6 Oct 2015 14:12:02 +0200 Subject: bridge: netlink: add support for port's multicast_router attribute Add IFLA_BRPORT_MULTICAST_ROUTER to allow setting/getting port's multicast_router via netlink. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index fcea39a..e3b6217 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -304,6 +304,7 @@ enum { IFLA_BRPORT_FORWARD_DELAY_TIMER, IFLA_BRPORT_HOLD_TIMER, IFLA_BRPORT_FLUSH, + IFLA_BRPORT_MULTICAST_ROUTER, __IFLA_BRPORT_MAX }; #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 6468166..d78b442 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -138,6 +138,9 @@ static inline size_t br_port_info_size(void) + nla_total_size(sizeof(u64)) /* IFLA_BRPORT_MESSAGE_AGE_TIMER */ + nla_total_size(sizeof(u64)) /* IFLA_BRPORT_FORWARD_DELAY_TIMER */ + nla_total_size(sizeof(u64)) /* IFLA_BRPORT_HOLD_TIMER */ +#ifdef CONFIG_BRIDGE_IGMP_SNOOPING + + nla_total_size(sizeof(u8)) /* IFLA_BRPORT_MULTICAST_ROUTER */ +#endif + 0; } @@ -196,6 +199,12 @@ static int br_port_fill_attrs(struct sk_buff *skb, if (nla_put_u64(skb, IFLA_BRPORT_HOLD_TIMER, timerval)) return -EMSGSIZE; +#ifdef CONFIG_BRIDGE_IGMP_SNOOPING + if (nla_put_u8(skb, IFLA_BRPORT_MULTICAST_ROUTER, + p->multicast_router)) + return -EMSGSIZE; +#endif + return 0; } @@ -560,6 +569,7 @@ static const struct nla_policy br_port_policy[IFLA_BRPORT_MAX + 1] = { [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NLA_U8 }, [IFLA_BRPORT_PROXYARP] = { .type = NLA_U8 }, [IFLA_BRPORT_PROXYARP_WIFI] = { .type = NLA_U8 }, + [IFLA_BRPORT_MULTICAST_ROUTER] = { .type = NLA_U8 }, }; /* Change the state of the port and notify spanning tree */ @@ -634,6 +644,15 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) if (tb[IFLA_BRPORT_FLUSH]) br_fdb_delete_by_port(p->br, p, 0, 0); +#ifdef CONFIG_BRIDGE_IGMP_SNOOPING + if (tb[IFLA_BRPORT_MULTICAST_ROUTER]) { + u8 mcast_router = nla_get_u8(tb[IFLA_BRPORT_MULTICAST_ROUTER]); + + err = br_multicast_set_port_router(p, mcast_router); + if (err) + return err; + } +#endif br_port_flags_change(p, old_flags ^ p->flags); return 0; } -- cgit v0.10.2 From 9abfa8cb201b2167872654b92f567f8bff6016f4 Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Tue, 6 Oct 2015 15:12:27 +0200 Subject: s390/qeth: switch to napi_gro_receive Add support for GRO (generic receive offload) in the layer 2 part of device driver qeth. This results in a performance improvement when GRO and RX is turned on. Signed-off-by: Thomas Richter Signed-off-by: Ursula Braun Signed-off-by: David S. Miller diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index dc905b3..b6eabfa 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -511,7 +511,7 @@ static int qeth_l2_process_inbound_buffer(struct qeth_card *card, if (skb->protocol == htons(ETH_P_802_2)) *((__u32 *)skb->cb) = ++card->seqno.pkt_seqno; len = skb->len; - netif_receive_skb(skb); + napi_gro_receive(&card->napi, skb); break; case QETH_HEADER_TYPE_OSN: if (card->info.type == QETH_CARD_TYPE_OSN) { -- cgit v0.10.2 From fe5c802882f2eaaf5475b072f16066038754782a Mon Sep 17 00:00:00 2001 From: Lakhvich Dmitriy Date: Tue, 6 Oct 2015 15:12:28 +0200 Subject: s390/qeth: optimize MAC handling in rx_mode callback In layer2 mode of the qeth driver, MAC address lists from struct net_device require mapping to the OSA-card. The existing implementation is inefficient for lists with more than several MAC addresses, since for every ndo_set_rx_mode callback it removes all MAC addresses first, and then registers the current MAC address list. This patch changes implementation of ndo_set_rx_mode callback in qeth, only performing hardware registration/removal for new/deleted addresses. To shorten lookup of MAC addresses registered addresses are kept in a hashtable instead of a linear list. Signed-off-by: Lakhvich Dmitriy Signed-off-by: Ursula Braun Reviewed-by: Eugene Crosser Reviewed-by: Thomas Richter Tested-by: Christian Borntraeger Signed-off-by: David S. Miller diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 6719447d..1766a20 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -739,11 +740,17 @@ struct qeth_vlan_vid { unsigned short vid; }; -struct qeth_mc_mac { - struct list_head list; - __u8 mc_addr[MAX_ADDR_LEN]; - unsigned char mc_addrlen; - int is_vmac; +enum qeth_mac_disposition { + QETH_DISP_MAC_DELETE = 0, + QETH_DISP_MAC_DO_NOTHING = 1, + QETH_DISP_MAC_ADD = 2, +}; + +struct qeth_mac { + u8 mac_addr[OSA_ADDR_LEN]; + u8 is_uc:1; + u8 disp_flag:2; + struct hlist_node hnode; }; struct qeth_rx { @@ -790,7 +797,7 @@ struct qeth_card { spinlock_t mclock; unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; struct list_head vid_list; - struct list_head mc_list; + DECLARE_HASHTABLE(mac_htable, 4); struct work_struct kernel_thread_starter; spinlock_t thread_mask_lock; unsigned long thread_start_mask; diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index b6eabfa..8f1b091 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -19,7 +19,9 @@ #include #include #include - +#include +#include +#include #include "qeth_core.h" #include "qeth_l2.h" @@ -28,7 +30,7 @@ static int qeth_l2_stop(struct net_device *); static int qeth_l2_send_delmac(struct qeth_card *, __u8 *); static int qeth_l2_send_setdelmac(struct qeth_card *, __u8 *, enum qeth_ipa_cmds); -static void qeth_l2_set_multicast_list(struct net_device *); +static void qeth_l2_set_rx_mode(struct net_device *); static int qeth_l2_recover(void *); static void qeth_bridgeport_query_support(struct qeth_card *card); static void qeth_bridge_state_change(struct qeth_card *card, @@ -193,49 +195,44 @@ static int qeth_l2_send_delgroupmac(struct qeth_card *card, __u8 *mac) return rc; } -static void qeth_l2_add_mc(struct qeth_card *card, __u8 *mac, int vmac) +static inline u32 qeth_l2_mac_hash(const u8 *addr) { - struct qeth_mc_mac *mc; - int rc; - - mc = kmalloc(sizeof(struct qeth_mc_mac), GFP_ATOMIC); + return get_unaligned((u32 *)(&addr[2])); +} - if (!mc) - return; +static int qeth_l2_write_mac(struct qeth_card *card, struct qeth_mac *mac) +{ - memcpy(mc->mc_addr, mac, OSA_ADDR_LEN); - mc->mc_addrlen = OSA_ADDR_LEN; - mc->is_vmac = vmac; + int rc; - if (vmac) { + if (mac->is_uc) { rc = qeth_setdel_makerc(card, - qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC)); + qeth_l2_send_setdelmac(card, mac->mac_addr, + IPA_CMD_SETVMAC)); } else { rc = qeth_setdel_makerc(card, - qeth_l2_send_setgroupmac(card, mac)); + qeth_l2_send_setgroupmac(card, mac->mac_addr)); } - - if (!rc) - list_add_tail(&mc->list, &card->mc_list); - else - kfree(mc); + return rc; } -static void qeth_l2_del_all_mc(struct qeth_card *card, int del) +static void qeth_l2_del_all_macs(struct qeth_card *card, int del) { - struct qeth_mc_mac *mc, *tmp; + struct qeth_mac *mac; + struct hlist_node *tmp; + int i; spin_lock_bh(&card->mclock); - list_for_each_entry_safe(mc, tmp, &card->mc_list, list) { + hash_for_each_safe(card->mac_htable, i, tmp, mac, hnode) { if (del) { - if (mc->is_vmac) - qeth_l2_send_setdelmac(card, mc->mc_addr, - IPA_CMD_DELVMAC); + if (mac->is_uc) + qeth_l2_send_setdelmac(card, mac->mac_addr, + IPA_CMD_DELVMAC); else - qeth_l2_send_delgroupmac(card, mc->mc_addr); + qeth_l2_send_delgroupmac(card, mac->mac_addr); } - list_del(&mc->list); - kfree(mc); + hash_del(&mac->hnode); + kfree(mac); } spin_unlock_bh(&card->mclock); } @@ -403,7 +400,7 @@ static int qeth_l2_vlan_rx_kill_vid(struct net_device *dev, rc = qeth_l2_send_setdelvlan(card, vid, IPA_CMD_DELVLAN); kfree(tmpid); } - qeth_l2_set_multicast_list(card->dev); + qeth_l2_set_rx_mode(card->dev); return rc; } @@ -460,7 +457,7 @@ static void qeth_l2_stop_card(struct qeth_card *card, int recovery_mode) card->state = CARD_STATE_SOFTSETUP; } if (card->state == CARD_STATE_SOFTSETUP) { - qeth_l2_del_all_mc(card, 0); + qeth_l2_del_all_macs(card, 0); qeth_clear_ipacmd_list(card); card->state = CARD_STATE_HARDSETUP; } @@ -768,29 +765,91 @@ static void qeth_promisc_to_bridge(struct qeth_card *card) card->options.sbp.role = role; card->info.promisc_mode = promisc_mode; } + +} +/* New MAC address is added to the hash table and marked to be written on card + * only if there is not in the hash table storage already + * +*/ +static void +qeth_l2_add_mac(struct qeth_card *card, struct netdev_hw_addr *ha, u8 is_uc) +{ + struct qeth_mac *mac; + + hash_for_each_possible(card->mac_htable, mac, hnode, + qeth_l2_mac_hash(ha->addr)) { + if (is_uc == mac->is_uc && + !memcmp(ha->addr, mac->mac_addr, OSA_ADDR_LEN)) { + mac->disp_flag = QETH_DISP_MAC_DO_NOTHING; + return; + } + } + + mac = kzalloc(sizeof(struct qeth_mac), GFP_ATOMIC); + + if (!mac) + return; + + memcpy(mac->mac_addr, ha->addr, OSA_ADDR_LEN); + mac->is_uc = is_uc; + mac->disp_flag = QETH_DISP_MAC_ADD; + + hash_add(card->mac_htable, &mac->hnode, + qeth_l2_mac_hash(mac->mac_addr)); + } -static void qeth_l2_set_multicast_list(struct net_device *dev) +static void qeth_l2_set_rx_mode(struct net_device *dev) { struct qeth_card *card = dev->ml_priv; struct netdev_hw_addr *ha; + struct qeth_mac *mac; + struct hlist_node *tmp; + int i; + int rc; if (card->info.type == QETH_CARD_TYPE_OSN) - return ; + return; QETH_CARD_TEXT(card, 3, "setmulti"); if (qeth_threads_running(card, QETH_RECOVER_THREAD) && (card->state != CARD_STATE_UP)) return; - qeth_l2_del_all_mc(card, 1); + spin_lock_bh(&card->mclock); + netdev_for_each_mc_addr(ha, dev) - qeth_l2_add_mc(card, ha->addr, 0); + qeth_l2_add_mac(card, ha, 0); netdev_for_each_uc_addr(ha, dev) - qeth_l2_add_mc(card, ha->addr, 1); + qeth_l2_add_mac(card, ha, 1); + + hash_for_each_safe(card->mac_htable, i, tmp, mac, hnode) { + if (mac->disp_flag == QETH_DISP_MAC_DELETE) { + if (!mac->is_uc) + rc = qeth_l2_send_delgroupmac(card, + mac->mac_addr); + else { + rc = qeth_l2_send_setdelmac(card, mac->mac_addr, + IPA_CMD_DELVMAC); + } + + hash_del(&mac->hnode); + kfree(mac); + + } else if (mac->disp_flag == QETH_DISP_MAC_ADD) { + rc = qeth_l2_write_mac(card, mac); + if (rc) { + hash_del(&mac->hnode); + kfree(mac); + } else + mac->disp_flag = QETH_DISP_MAC_DELETE; + } else + mac->disp_flag = QETH_DISP_MAC_DELETE; + } spin_unlock_bh(&card->mclock); + if (qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE)) qeth_setadp_promisc_mode(card); else @@ -974,7 +1033,7 @@ static int qeth_l2_probe_device(struct ccwgroup_device *gdev) qeth_l2_create_device_attributes(&gdev->dev); INIT_LIST_HEAD(&card->vid_list); - INIT_LIST_HEAD(&card->mc_list); + hash_init(card->mac_htable); card->options.layer2 = 1; card->info.hwtrap = 0; return 0; @@ -1020,7 +1079,7 @@ static const struct net_device_ops qeth_l2_netdev_ops = { .ndo_get_stats = qeth_get_stats, .ndo_start_xmit = qeth_l2_hard_start_xmit, .ndo_validate_addr = eth_validate_addr, - .ndo_set_rx_mode = qeth_l2_set_multicast_list, + .ndo_set_rx_mode = qeth_l2_set_rx_mode, .ndo_do_ioctl = qeth_l2_do_ioctl, .ndo_set_mac_address = qeth_l2_set_mac_address, .ndo_change_mtu = qeth_change_mtu, @@ -1179,7 +1238,7 @@ contin: rtnl_unlock(); } /* this also sets saved unicast addresses */ - qeth_l2_set_multicast_list(card->dev); + qeth_l2_set_rx_mode(card->dev); } /* let user_space know that device is online */ kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE); -- cgit v0.10.2 From ec6674c6382819542446b727a2bf54f3dd879ca2 Mon Sep 17 00:00:00 2001 From: Eugene Crosser Date: Tue, 6 Oct 2015 15:12:29 +0200 Subject: s390/qdio: fix WARN_ON_ONCE condition If HiperSockets Completion Queueing is enabled, qdio always issues a warning, since the condition is always met. This patch fixes the condition in WARN_ON_ONCE that was always true. Signed-off-by: Eugene Crosser Signed-off-by: Ursula Braun Signed-off-by: David S. Miller diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 848e3b6..4bb5262f 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -319,6 +319,8 @@ static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit, int retries = 0, cc; unsigned long laob = 0; + WARN_ON_ONCE(aob && ((queue_type(q) != QDIO_IQDIO_QFMT) || + !q->u.out.use_cq)); if (q->u.out.use_cq && aob != 0) { fc = QDIO_SIGA_WRITEQ; laob = aob; @@ -329,8 +331,6 @@ static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit, fc |= QDIO_SIGA_QEBSM_FLAG; } again: - WARN_ON_ONCE((aob && queue_type(q) != QDIO_IQDIO_QFMT) || - (aob && fc != QDIO_SIGA_WRITEQ)); cc = do_siga_output(schid, q->mask, busy_bit, fc, laob); /* hipersocket busy condition */ -- cgit v0.10.2 From 686a562449af96a0e8c18c6f1b87b47ff8c36de8 Mon Sep 17 00:00:00 2001 From: Yuvaraja Mariappan Date: Tue, 6 Oct 2015 10:53:29 -0700 Subject: net: ipv4: tcp.c Fixed an assignment coding style issue Fixed an assignment coding style issue Signed-off-by: Yuvaraja Mariappan Signed-off-by: David S. Miller diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 3c96fa8..ac1bdbb 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -900,7 +900,8 @@ static ssize_t do_tcp_sendpages(struct sock *sk, struct page *page, int offset, */ if (((1 << sk->sk_state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) && !tcp_passive_fastopen(sk)) { - if ((err = sk_stream_wait_connect(sk, &timeo)) != 0) + err = sk_stream_wait_connect(sk, &timeo); + if (err != 0) goto out_err; } @@ -967,7 +968,8 @@ new_segment: copied += copy; offset += copy; - if (!(size -= copy)) { + size -= copy; + if (!size) { tcp_tx_timestamp(sk, skb); goto out; } @@ -988,7 +990,8 @@ wait_for_memory: tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH, size_goal); - if ((err = sk_stream_wait_memory(sk, &timeo)) != 0) + err = sk_stream_wait_memory(sk, &timeo); + if (err != 0) goto do_error; mss_now = tcp_send_mss(sk, &size_goal, flags); @@ -1111,7 +1114,8 @@ int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) */ if (((1 << sk->sk_state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) && !tcp_passive_fastopen(sk)) { - if ((err = sk_stream_wait_connect(sk, &timeo)) != 0) + err = sk_stream_wait_connect(sk, &timeo); + if (err != 0) goto do_error; } @@ -1267,7 +1271,8 @@ wait_for_memory: tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH, size_goal); - if ((err = sk_stream_wait_memory(sk, &timeo)) != 0) + err = sk_stream_wait_memory(sk, &timeo); + if (err != 0) goto do_error; mss_now = tcp_send_mss(sk, &size_goal, flags); @@ -1767,7 +1772,8 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock, /* __ Restore normal policy in scheduler __ */ - if ((chunk = len - tp->ucopy.len) != 0) { + chunk = len - tp->ucopy.len; + if (chunk != 0) { NET_ADD_STATS_USER(sock_net(sk), LINUX_MIB_TCPDIRECTCOPYFROMBACKLOG, chunk); len -= chunk; copied += chunk; @@ -1778,7 +1784,8 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock, do_prequeue: tcp_prequeue_process(sk); - if ((chunk = len - tp->ucopy.len) != 0) { + chunk = len - tp->ucopy.len; + if (chunk != 0) { NET_ADD_STATS_USER(sock_net(sk), LINUX_MIB_TCPDIRECTCOPYFROMPREQUEUE, chunk); len -= chunk; copied += chunk; @@ -2230,7 +2237,8 @@ int tcp_disconnect(struct sock *sk, int flags) sk->sk_shutdown = 0; sock_reset_flag(sk, SOCK_DONE); tp->srtt_us = 0; - if ((tp->write_seq += tp->max_window + 2) == 0) + tp->write_seq += tp->max_window + 2; + if (tp->write_seq == 0) tp->write_seq = 1; icsk->icsk_backoff = 0; tp->snd_cwnd = 2; -- cgit v0.10.2 From acb4a6bfc80ddeea4c44074dd630f916259e909e Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 6 Oct 2015 14:49:58 -0700 Subject: tcp: ensure prior synack rtx behavior with small backlogs Some applications use a listen() backlog of 1. Prior kernels were silently enforcing a qlen_log of 4, so that we were sending up to /proc/sys/net/ipv4/tcp_synack_retries SYNACK messages. Fixes: ef547f2ac16b ("tcp: remove max_qlen_log") Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 89eedfb..514b9e9 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -579,7 +579,7 @@ static void reqsk_timer_handler(unsigned long data) * ones are about to clog our table. */ qlen = reqsk_queue_len(queue); - if ((qlen << 1) > sk_listener->sk_max_ack_backlog) { + if ((qlen << 1) > max(8U, sk_listener->sk_max_ack_backlog)) { int young = reqsk_queue_len_young(queue) << 1; while (thresh > 2) { -- cgit v0.10.2 From 22e05bd6f7949d3e638c9f4697a2198a19fff312 Mon Sep 17 00:00:00 2001 From: Paul M Stillwell Jr Date: Wed, 26 Aug 2015 15:14:07 -0400 Subject: i40e: Increase the amount of time we wait for reset to be done In some rare cases the reset can take longer to complete so increase the amount of time we wait. Change-ID: Ib5628ec54b526a811ee33d1214fe763226406671 Signed-off-by: Paul M Stillwell Jr Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 6833717..1e81f4e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -1256,7 +1256,7 @@ i40e_status i40e_pf_reset(struct i40e_hw *hw) grst_del = (rd32(hw, I40E_GLGEN_RSTCTL) & I40E_GLGEN_RSTCTL_GRSTDEL_MASK) >> I40E_GLGEN_RSTCTL_GRSTDEL_SHIFT; - for (cnt = 0; cnt < grst_del + 2; cnt++) { + for (cnt = 0; cnt < grst_del + 10; cnt++) { reg = rd32(hw, I40E_GLGEN_RSTAT); if (!(reg & I40E_GLGEN_RSTAT_DEVSTATE_MASK)) break; -- cgit v0.10.2 From 1d5109d18757d8d6450905275bf362819781368c Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Wed, 26 Aug 2015 15:14:08 -0400 Subject: i40e: enable WoL operation if config bit show WoL capable The driver was disabling Wake-on-LAN by default and waiting for the user to expressly turn it on. This patch has the driver turning on WoL from the start if enabled in the hardware config, which matches the behavior of our other drivers. Change-ID: I43faedb907f8ba4d1a61b72a7c86072b97af12b1 Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 5646ee8..7fc5e2c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -9947,6 +9947,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) struct i40e_pf *pf; struct i40e_hw *hw; static u16 pfs_found; + u16 wol_nvm_bits; u16 link_status; int err = 0; u32 len; @@ -10163,8 +10164,12 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) clear_bit(__I40E_SERVICE_SCHED, &pf->state); pf->flags |= I40E_FLAG_NEED_LINK_UPDATE; - /* WoL defaults to disabled */ - pf->wol_en = false; + /* NVM bit on means WoL disabled for the port */ + i40e_read_nvm_word(hw, I40E_SR_NVM_WAKE_ON_LAN, &wol_nvm_bits); + if ((1 << hw->port) & wol_nvm_bits || hw->partition_id != 1) + pf->wol_en = false; + else + pf->wol_en = true; device_set_wakeup_enable(&pf->pdev->dev, pf->wol_en); /* set up the main switch operations */ -- cgit v0.10.2 From 0325fca7577c5157da2cdf064fc3e8d38242e47c Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Wed, 26 Aug 2015 15:14:09 -0400 Subject: i40e: stop VF rings Explicitly stop the rings belonging to each VF when disabling SR-IOV. Even though the VFs were gone, and the associated VSIs were removed, the rings were not stopped, and in some circumstances the hardware would continue to access the memory formerly used by the rings, causing memory corruption or DMAR errors, both of which would lead to general malaise of the kernel. To relieve this condition, explicitly stop all the rings associated with each VF before releasing its resources. Change-ID: I78c05d562c66e7b594b7e48d67860f49b3e5b6ec Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index b148694..0545e3f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -872,6 +872,11 @@ void i40e_free_vfs(struct i40e_pf *pf) i40e_vsi_control_rings(pf->vsi[pf->vf[i].lan_vsi_idx], false); + 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); + /* Disable IOV before freeing resources. This lets any VF drivers * running in the host get themselves cleaned up before we yank * the carpet out from underneath their feet. -- cgit v0.10.2 From bc5166b908aaa126d524b84c767323b75c17bdee Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Wed, 26 Aug 2015 15:14:10 -0400 Subject: i40e/i40evf: add driver support for new device ids Early addition of new a device id. Change-ID: I61a8c8556fdf4f5714be4e4089689e374f30293c Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 1e81f4e..7a70abc 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -51,6 +51,7 @@ static i40e_status i40e_set_mac_type(struct i40e_hw *hw) case I40E_DEV_ID_QSFP_B: case I40E_DEV_ID_QSFP_C: case I40E_DEV_ID_10G_BASE_T: + case I40E_DEV_ID_10G_BASE_T4: case I40E_DEV_ID_20G_KR2: case I40E_DEV_ID_20G_KR2_A: hw->mac.type = I40E_MAC_XL710; diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 1345de2..928bd5a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -425,6 +425,7 @@ static void i40e_get_settings_link_down(struct i40e_hw *hw, ecmd->advertising = ADVERTISED_10000baseKR_Full; break; case I40E_DEV_ID_10G_BASE_T: + case I40E_DEV_ID_10G_BASE_T4: ecmd->supported = SUPPORTED_10000baseT_Full | SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 7fc5e2c..19eb14d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -75,6 +75,7 @@ static const struct pci_device_id i40e_pci_tbl[] = { {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_B), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_C), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T), 0}, + {PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T4), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_20G_KR2), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_X722), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_1G_BASE_T_X722), 0}, diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index c5b6a65..34720e0 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -46,6 +46,7 @@ #define I40E_DEV_ID_10G_BASE_T 0x1586 #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_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_common.c b/drivers/net/ethernet/intel/i40evf/i40e_common.c index 1950db1..96e48ee 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_common.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_common.c @@ -51,6 +51,7 @@ i40e_status i40e_set_mac_type(struct i40e_hw *hw) case I40E_DEV_ID_QSFP_B: case I40E_DEV_ID_QSFP_C: case I40E_DEV_ID_10G_BASE_T: + case I40E_DEV_ID_10G_BASE_T4: case I40E_DEV_ID_20G_KR2: case I40E_DEV_ID_20G_KR2_A: hw->mac.type = I40E_MAC_XL710; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_type.h b/drivers/net/ethernet/intel/i40evf/i40e_type.h index 37bacc3..bbb3886 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_type.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_type.h @@ -46,6 +46,7 @@ #define I40E_DEV_ID_10G_BASE_T 0x1586 #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_VF 0x154C #define I40E_DEV_ID_VF_HV 0x1571 #define I40E_DEV_ID_SFP_X722 0x37D0 -- cgit v0.10.2 From df08fd4dbcc95cbc5a35745b6a77a9df0dc5776f Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Wed, 26 Aug 2015 15:14:11 -0400 Subject: i40e/i40evf: fix a potential type compare issue Rework an if expression to assure there is no type compare problem between a size and a possible negative number. Change-ID: I4921fcc96abfcf69490efce020a9e4007f251c99 Reported-by: Helin Zhang Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.h b/drivers/net/ethernet/intel/i40e/i40e_adminq.h index ca81b0b..12fbbdd 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq.h +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.h @@ -144,8 +144,7 @@ static inline int i40e_aq_rc_to_posix(int aq_ret, int aq_rc) if (aq_ret == I40E_ERR_ADMIN_QUEUE_TIMEOUT) return -EAGAIN; - if (aq_rc >= (sizeof(aq_to_posix) / sizeof((aq_to_posix)[0])) || - aq_rc < 0) + if (!((u32)aq_rc < (sizeof(aq_to_posix) / sizeof((aq_to_posix)[0])))) return -ERANGE; return aq_to_posix[aq_rc]; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq.h b/drivers/net/ethernet/intel/i40evf/i40e_adminq.h index e62e951..a3eae5d 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq.h @@ -144,8 +144,7 @@ static inline int i40e_aq_rc_to_posix(int aq_ret, int aq_rc) if (aq_ret == I40E_ERR_ADMIN_QUEUE_TIMEOUT) return -EAGAIN; - if (aq_rc >= (sizeof(aq_to_posix) / sizeof((aq_to_posix)[0])) || - aq_rc < 0) + if (!((u32)aq_rc < (sizeof(aq_to_posix) / sizeof((aq_to_posix)[0])))) return -ERANGE; return aq_to_posix[aq_rc]; -- cgit v0.10.2 From 725821f340184fcdd35dbd369c5058eec8a30fcb Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Wed, 26 Aug 2015 15:14:12 -0400 Subject: i40e: fix bad CEE status shift value Fix a shift value that was wrong, ending up with a bad bitmask. Also add a blank line between two sets of #defines for better readability. Change-ID: I3e41fa2a2ab904d3a4e6cbf13972ab0036a10601 Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h index b840fab..785b3db 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h @@ -2062,6 +2062,7 @@ I40E_CHECK_CMD_LENGTH(i40e_aqc_lldp_start); #define I40E_AQC_CEE_APP_ISCSI_MASK (0x7 << I40E_AQC_CEE_APP_ISCSI_SHIFT) #define I40E_AQC_CEE_APP_FIP_SHIFT 0x8 #define I40E_AQC_CEE_APP_FIP_MASK (0x7 << I40E_AQC_CEE_APP_FIP_SHIFT) + #define I40E_AQC_CEE_PG_STATUS_SHIFT 0x0 #define I40E_AQC_CEE_PG_STATUS_MASK (0x7 << I40E_AQC_CEE_PG_STATUS_SHIFT) #define I40E_AQC_CEE_PFC_STATUS_SHIFT 0x3 @@ -2070,7 +2071,7 @@ I40E_CHECK_CMD_LENGTH(i40e_aqc_lldp_start); #define I40E_AQC_CEE_APP_STATUS_MASK (0x7 << I40E_AQC_CEE_APP_STATUS_SHIFT) #define I40E_AQC_CEE_FCOE_STATUS_SHIFT 0x8 #define I40E_AQC_CEE_FCOE_STATUS_MASK (0x7 << I40E_AQC_CEE_FCOE_STATUS_SHIFT) -#define I40E_AQC_CEE_ISCSI_STATUS_SHIFT 0xA +#define I40E_AQC_CEE_ISCSI_STATUS_SHIFT 0xB #define I40E_AQC_CEE_ISCSI_STATUS_MASK (0x7 << I40E_AQC_CEE_ISCSI_STATUS_SHIFT) #define I40E_AQC_CEE_FIP_STATUS_SHIFT 0x10 #define I40E_AQC_CEE_FIP_STATUS_MASK (0x7 << I40E_AQC_CEE_FIP_STATUS_SHIFT) -- cgit v0.10.2 From 21364bcfc7810894114ab18f5335745e374cc2ea Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Wed, 26 Aug 2015 15:14:13 -0400 Subject: i40e: make i40e_init_pf_fcoe to void i40e_init_pf_fcoe() didn't return anything except 0, it prints enough error info already, and no driver logic depends on the return value, so this can be void. Change-ID: Ie6afad849857d87a7064c42c3cce14c74c2f29d8 Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index f6d97ad..44837ec 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -740,7 +740,7 @@ int i40e_fcoe_vsi_init(struct i40e_vsi *vsi, struct i40e_vsi_context *ctxt); u8 i40e_get_fcoe_tc_map(struct i40e_pf *pf); void i40e_fcoe_config_netdev(struct net_device *netdev, struct i40e_vsi *vsi); void i40e_fcoe_vsi_setup(struct i40e_pf *pf); -int i40e_init_pf_fcoe(struct i40e_pf *pf); +void i40e_init_pf_fcoe(struct i40e_pf *pf); int i40e_fcoe_setup_ddp_resources(struct i40e_vsi *vsi); void i40e_fcoe_free_ddp_resources(struct i40e_vsi *vsi); int i40e_fcoe_handle_offload(struct i40e_ring *rx_ring, diff --git a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c b/drivers/net/ethernet/intel/i40e/i40e_fcoe.c index 5ea75dd..2398d9b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c +++ b/drivers/net/ethernet/intel/i40e/i40e_fcoe.c @@ -272,10 +272,8 @@ out: /** * i40e_fcoe_sw_init - sets up the HW for FCoE * @pf: pointer to PF - * - * Returns 0 if FCoE is supported otherwise the error code **/ -int i40e_init_pf_fcoe(struct i40e_pf *pf) +void i40e_init_pf_fcoe(struct i40e_pf *pf) { struct i40e_hw *hw = &pf->hw; u32 val; @@ -287,13 +285,13 @@ int i40e_init_pf_fcoe(struct i40e_pf *pf) if (!pf->hw.func_caps.fcoe) { dev_info(&pf->pdev->dev, "FCoE capability is disabled\n"); - return 0; + return; } if (!pf->hw.func_caps.dcb) { dev_warn(&pf->pdev->dev, "Hardware is not DCB capable not enabling FCoE.\n"); - return 0; + return; } /* enable FCoE hash filter */ @@ -326,7 +324,7 @@ int i40e_init_pf_fcoe(struct i40e_pf *pf) wr32(hw, I40E_GLFCOE_RCTL, val); dev_info(&pf->pdev->dev, "FCoE is supported.\n"); - return 0; + return; } /** diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 19eb14d..0ef6a15 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -6573,9 +6573,7 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit) } #endif /* CONFIG_I40E_DCB */ #ifdef I40E_FCOE - ret = i40e_init_pf_fcoe(pf); - if (ret) - dev_info(&pf->pdev->dev, "init_pf_fcoe failed: %d\n", ret); + i40e_init_pf_fcoe(pf); #endif /* do basic switch setup */ @@ -7976,9 +7974,7 @@ static int i40e_sw_init(struct i40e_pf *pf) } #ifdef I40E_FCOE - err = i40e_init_pf_fcoe(pf); - if (err) - dev_info(&pf->pdev->dev, "init_pf_fcoe failed: %d\n", err); + i40e_init_pf_fcoe(pf); #endif /* I40E_FCOE */ #ifdef CONFIG_PCI_IOV -- cgit v0.10.2 From 9e1c26e35030aa56057a69514acb17da5e81eb16 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Wed, 26 Aug 2015 15:14:14 -0400 Subject: i40e/i40evf: assure clean asq status report There was a possibility where the asq_last_status could get through without update and thus report a previous error. I don't think we've actually seen this happen, but this patch will help make sure it doesn't. Change-ID: I9e33927052a5ee6ea21f80b66d4c4b76c2760b17 Signed-off-by: Shannon Nelson Signed-off-by: Christopher Pau Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.c b/drivers/net/ethernet/intel/i40e/i40e_adminq.c index 34a64e0..287cb8d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq.c +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.c @@ -756,6 +756,8 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw, goto asq_send_command_error; } + hw->aq.asq_last_status = I40E_AQ_RC_OK; + val = rd32(hw, hw->aq.asq.head); if (val >= hw->aq.num_asq_entries) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq.c b/drivers/net/ethernet/intel/i40evf/i40e_adminq.c index c7f59e0..c5cf886 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq.c @@ -696,6 +696,8 @@ i40e_status i40evf_asq_send_command(struct i40e_hw *hw, goto asq_send_command_error; } + hw->aq.asq_last_status = I40E_AQ_RC_OK; + val = rd32(hw, hw->aq.asq.head); if (val >= hw->aq.num_asq_entries) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, -- cgit v0.10.2 From fba52e21e5cad1347255d0d3600f7e4df6e69435 Mon Sep 17 00:00:00 2001 From: Carolyn Wyborny Date: Wed, 26 Aug 2015 15:14:15 -0400 Subject: i40e: Fix for truncated interrupt name This patch extends the size of the text available for the interrupt names. Without this patch, all the descriptive data available for the Flow Director interrupts is truncated. Change-ID: I2ac458f23ac3b4ea8f1edf73edc283b1d3704c7f Signed-off-by: Carolyn Wyborny Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 44837ec..a54577a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -97,7 +97,7 @@ #define I40E_MAX_USER_PRIORITY 8 #define I40E_DEFAULT_MSG_ENABLE 4 #define I40E_QUEUE_WAIT_RETRY_LIMIT 10 -#define I40E_INT_NAME_STR_LEN (IFNAMSIZ + 9) +#define I40E_INT_NAME_STR_LEN (IFNAMSIZ + 16) /* Ethtool Private Flags */ #define I40E_PRIV_FLAGS_NPAR_FLAG BIT(0) -- cgit v0.10.2 From 0002e1189b594d462c7a93417f5ac8bbc22bd768 Mon Sep 17 00:00:00 2001 From: Catherine Sullivan Date: Wed, 26 Aug 2015 15:14:16 -0400 Subject: i40e: Never let speed get set to 0 in get_settings In ethtool, there is a possibility of speed getting set to 0 if advertise is set to 0 (which it is when autoneg is disabled). We never want this to happen as the firmware will actually attempt to set the speed to 0 sending link down, so add an extra check to make sure this doesn't happen. Change-ID: I62e0eeee2cbf043d8e6f5c9c9f0b92794e877f01 Signed-off-by: Catherine Sullivan Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 928bd5a..930369c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -666,6 +666,13 @@ static int i40e_set_settings(struct net_device *netdev, advertise & ADVERTISED_40000baseLR4_Full) config.link_speed |= I40E_LINK_SPEED_40GB; + /* If speed didn't get set, set it to what it currently is. + * This is needed because if advertise is 0 (as it is when autoneg + * is disabled) then speed won't get set. + */ + if (!config.link_speed) + config.link_speed = abilities.link_speed; + if (change || (abilities.link_speed != config.link_speed)) { /* copy over the rest of the abilities */ config.phy_type = abilities.phy_type; -- cgit v0.10.2 From fb43201f150300fa2c0215f00418eda9143cdab1 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Wed, 26 Aug 2015 15:14:17 -0400 Subject: i40e/i40evf: give up the __func__ During early development, we added the function name to all of the error strings to make debugging simpler. Now that we've released the driver, our users should have more comprehensible error messages. So tear the roof off and give up the __func__. Ow. Change-ID: I7e1766252c7a032b9af6520da6aff536bdfd533c Signed-off-by: Shannon Nelson Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c b/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c index 1c51f73..dbadad7 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c @@ -240,9 +240,8 @@ static void i40e_dcbnl_del_app(struct i40e_pf *pf, if (pf->vsi[v] && pf->vsi[v]->netdev) { err = i40e_dcbnl_vsi_del_app(pf->vsi[v], app); if (err) - dev_info(&pf->pdev->dev, "%s: Failed deleting app for VSI seid=%d err=%d sel=%d proto=0x%x prio=%d\n", - __func__, pf->vsi[v]->seid, - err, app->selector, + dev_info(&pf->pdev->dev, "Failed deleting app for VSI seid=%d err=%d sel=%d proto=0x%x prio=%d\n", + pf->vsi[v]->seid, err, app->selector, app->protocolid, app->priority); } } diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 0ef6a15..1fa1eba 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -3156,8 +3156,7 @@ static int i40e_vsi_request_irq_msix(struct i40e_vsi *vsi, char *basename) q_vector); if (err) { dev_info(&pf->pdev->dev, - "%s: request_irq failed, error: %d\n", - __func__, err); + "MSIX request_irq failed, error: %d\n", err); goto free_queue_irqs; } /* assign the mask for this irq */ @@ -3681,9 +3680,8 @@ static int i40e_vsi_control_tx(struct i40e_vsi *vsi, bool enable) ret = i40e_pf_txq_wait(pf, pf_q, enable); if (ret) { dev_info(&pf->pdev->dev, - "%s: VSI seid %d Tx ring %d %sable timeout\n", - __func__, vsi->seid, pf_q, - (enable ? "en" : "dis")); + "VSI seid %d Tx ring %d %sable timeout\n", + vsi->seid, pf_q, (enable ? "en" : "dis")); break; } } @@ -3759,9 +3757,8 @@ static int i40e_vsi_control_rx(struct i40e_vsi *vsi, bool enable) ret = i40e_pf_rxq_wait(pf, pf_q, enable); if (ret) { dev_info(&pf->pdev->dev, - "%s: VSI seid %d Rx ring %d %sable timeout\n", - __func__, vsi->seid, pf_q, - (enable ? "en" : "dis")); + "VSI seid %d Rx ring %d %sable timeout\n", + vsi->seid, pf_q, (enable ? "en" : "dis")); break; } } @@ -4056,8 +4053,7 @@ static void i40e_quiesce_vsi(struct i40e_vsi *vsi) if ((test_bit(__I40E_PORT_TX_SUSPENDED, &vsi->back->state)) && vsi->type == I40E_VSI_FCOE) { dev_dbg(&vsi->back->pdev->dev, - "%s: VSI seid %d skipping FCoE VSI disable\n", - __func__, vsi->seid); + "VSI seid %d skipping FCoE VSI disable\n", vsi->seid); return; } @@ -4131,8 +4127,8 @@ static int i40e_vsi_wait_txq_disabled(struct i40e_vsi *vsi) ret = i40e_pf_txq_wait(pf, pf_q, false); if (ret) { dev_info(&pf->pdev->dev, - "%s: VSI seid %d Tx ring %d disable timeout\n", - __func__, vsi->seid, pf_q); + "VSI seid %d Tx ring %d disable timeout\n", + vsi->seid, pf_q); return ret; } } @@ -5423,8 +5419,7 @@ bool i40e_dcb_need_reconfig(struct i40e_pf *pf, dev_dbg(&pf->pdev->dev, "APP Table change detected.\n"); } - dev_dbg(&pf->pdev->dev, "%s: need_reconfig=%d\n", __func__, - need_reconfig); + dev_dbg(&pf->pdev->dev, "dcb need_reconfig=%d\n", need_reconfig); return need_reconfig; } @@ -5451,16 +5446,14 @@ static int i40e_handle_lldp_event(struct i40e_pf *pf, /* Ignore if event is not for Nearest Bridge */ type = ((mib->type >> I40E_AQ_LLDP_BRIDGE_TYPE_SHIFT) & I40E_AQ_LLDP_BRIDGE_TYPE_MASK); - dev_dbg(&pf->pdev->dev, - "%s: LLDP event mib bridge type 0x%x\n", __func__, type); + dev_dbg(&pf->pdev->dev, "LLDP event mib bridge type 0x%x\n", type); if (type != I40E_AQ_LLDP_BRIDGE_TYPE_NEAREST_BRIDGE) return ret; /* Check MIB Type and return if event for Remote MIB update */ type = mib->type & I40E_AQ_LLDP_MIB_TYPE_MASK; dev_dbg(&pf->pdev->dev, - "%s: LLDP event mib type %s\n", __func__, - type ? "remote" : "local"); + "LLDP event mib type %s\n", type ? "remote" : "local"); if (type == I40E_AQ_LLDP_MIB_REMOTE) { /* Update the remote cached instance and return */ ret = i40e_aq_get_dcb_config(hw, I40E_AQ_LLDP_MIB_REMOTE, @@ -9054,8 +9047,7 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, if (veb) { if (vsi->seid != pf->vsi[pf->lan_vsi]->seid) { dev_info(&vsi->back->pdev->dev, - "%s: New VSI creation error, uplink seid of LAN VSI expected.\n", - __func__); + "New VSI creation error, uplink seid of LAN VSI expected.\n"); return NULL; } /* We come up by default in VEPA mode if SRIOV is not @@ -10498,7 +10490,7 @@ static pci_ers_result_t i40e_pci_error_slot_reset(struct pci_dev *pdev) int err; u32 reg; - dev_info(&pdev->dev, "%s\n", __func__); + dev_dbg(&pdev->dev, "%s\n", __func__); if (pci_enable_device_mem(pdev)) { dev_info(&pdev->dev, "Cannot re-enable PCI device after reset.\n"); @@ -10538,7 +10530,7 @@ static void i40e_pci_error_resume(struct pci_dev *pdev) { struct i40e_pf *pf = pci_get_drvdata(pdev); - dev_info(&pdev->dev, "%s\n", __func__); + dev_dbg(&pdev->dev, "%s\n", __func__); if (test_bit(__I40E_SUSPENDED, &pf->state)) return; @@ -10630,9 +10622,7 @@ static int i40e_resume(struct pci_dev *pdev) err = pci_enable_device_mem(pdev); if (err) { - dev_err(&pdev->dev, - "%s: Cannot enable PCI device from suspend\n", - __func__); + dev_err(&pdev->dev, "Cannot enable PCI device from suspend\n"); return err; } pci_set_master(pdev); diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 76df6b2..f3a61c4 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -489,8 +489,7 @@ i40evf_request_traffic_irqs(struct i40evf_adapter *adapter, char *basename) q_vector); if (err) { dev_info(&adapter->pdev->dev, - "%s: request_irq failed, error: %d\n", - __func__, err); + "Request_irq failed, error: %d\n", err); goto free_queue_irqs; } /* assign the mask for this irq */ @@ -1853,8 +1852,7 @@ static int i40evf_setup_all_tx_resources(struct i40evf_adapter *adapter) if (!err) continue; dev_err(&adapter->pdev->dev, - "%s: Allocation for Tx Queue %u failed\n", - __func__, i); + "Allocation for Tx Queue %u failed\n", i); break; } @@ -1881,8 +1879,7 @@ static int i40evf_setup_all_rx_resources(struct i40evf_adapter *adapter) if (!err) continue; dev_err(&adapter->pdev->dev, - "%s: Allocation for Rx Queue %u failed\n", - __func__, i); + "Allocation for Rx Queue %u failed\n", i); break; } return err; diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index fbf2a1c..4f056ef 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -234,8 +234,8 @@ void i40evf_configure_queues(struct i40evf_adapter *adapter) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot configure queues, command %d pending\n", + adapter->current_op); return; } adapter->current_op = I40E_VIRTCHNL_OP_CONFIG_VSI_QUEUES; @@ -288,8 +288,8 @@ void i40evf_enable_queues(struct i40evf_adapter *adapter) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot enable queues, command %d pending\n", + adapter->current_op); return; } adapter->current_op = I40E_VIRTCHNL_OP_ENABLE_QUEUES; @@ -313,8 +313,8 @@ void i40evf_disable_queues(struct i40evf_adapter *adapter) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot disable queues, command %d pending\n", + adapter->current_op); return; } adapter->current_op = I40E_VIRTCHNL_OP_DISABLE_QUEUES; @@ -341,8 +341,8 @@ void i40evf_map_queues(struct i40evf_adapter *adapter) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot map queues to vectors, command %d pending\n", + adapter->current_op); return; } adapter->current_op = I40E_VIRTCHNL_OP_CONFIG_IRQ_MAP; @@ -393,8 +393,8 @@ void i40evf_add_ether_addrs(struct i40evf_adapter *adapter) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot add filters, command %d pending\n", + adapter->current_op); return; } list_for_each_entry(f, &adapter->mac_filter_list, list) { @@ -410,8 +410,7 @@ void i40evf_add_ether_addrs(struct i40evf_adapter *adapter) len = sizeof(struct i40e_virtchnl_ether_addr_list) + (count * sizeof(struct i40e_virtchnl_ether_addr)); if (len > I40EVF_MAX_AQ_BUF_SIZE) { - dev_warn(&adapter->pdev->dev, "%s: Too many MAC address changes in one request\n", - __func__); + dev_warn(&adapter->pdev->dev, "Too many add MAC changes in one request\n"); count = (I40EVF_MAX_AQ_BUF_SIZE - sizeof(struct i40e_virtchnl_ether_addr_list)) / sizeof(struct i40e_virtchnl_ether_addr); @@ -453,8 +452,8 @@ void i40evf_del_ether_addrs(struct i40evf_adapter *adapter) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot remove filters, command %d pending\n", + adapter->current_op); return; } list_for_each_entry(f, &adapter->mac_filter_list, list) { @@ -470,8 +469,7 @@ void i40evf_del_ether_addrs(struct i40evf_adapter *adapter) len = sizeof(struct i40e_virtchnl_ether_addr_list) + (count * sizeof(struct i40e_virtchnl_ether_addr)); if (len > I40EVF_MAX_AQ_BUF_SIZE) { - dev_warn(&adapter->pdev->dev, "%s: Too many MAC address changes in one request\n", - __func__); + dev_warn(&adapter->pdev->dev, "Too many delete MAC changes in one request\n"); count = (I40EVF_MAX_AQ_BUF_SIZE - sizeof(struct i40e_virtchnl_ether_addr_list)) / sizeof(struct i40e_virtchnl_ether_addr); @@ -513,8 +511,8 @@ void i40evf_add_vlans(struct i40evf_adapter *adapter) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot add VLANs, command %d pending\n", + adapter->current_op); return; } @@ -531,8 +529,7 @@ void i40evf_add_vlans(struct i40evf_adapter *adapter) len = sizeof(struct i40e_virtchnl_vlan_filter_list) + (count * sizeof(u16)); if (len > I40EVF_MAX_AQ_BUF_SIZE) { - dev_warn(&adapter->pdev->dev, "%s: Too many VLAN changes in one request\n", - __func__); + dev_warn(&adapter->pdev->dev, "Too many add VLAN changes in one request\n"); count = (I40EVF_MAX_AQ_BUF_SIZE - sizeof(struct i40e_virtchnl_vlan_filter_list)) / sizeof(u16); @@ -572,8 +569,8 @@ void i40evf_del_vlans(struct i40evf_adapter *adapter) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot remove VLANs, command %d pending\n", + adapter->current_op); return; } @@ -590,8 +587,7 @@ void i40evf_del_vlans(struct i40evf_adapter *adapter) len = sizeof(struct i40e_virtchnl_vlan_filter_list) + (count * sizeof(u16)); if (len > I40EVF_MAX_AQ_BUF_SIZE) { - dev_warn(&adapter->pdev->dev, "%s: Too many VLAN changes in one request\n", - __func__); + dev_warn(&adapter->pdev->dev, "Too many delete VLAN changes in one request\n"); count = (I40EVF_MAX_AQ_BUF_SIZE - sizeof(struct i40e_virtchnl_vlan_filter_list)) / sizeof(u16); @@ -629,8 +625,8 @@ void i40evf_set_promiscuous(struct i40evf_adapter *adapter, int flags) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot set promiscuous mode, command %d pending\n", + adapter->current_op); return; } adapter->current_op = I40E_VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE; @@ -720,17 +716,16 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter, } break; default: - dev_err(&adapter->pdev->dev, - "%s: Unknown event %d from pf\n", - __func__, vpe->event); + dev_err(&adapter->pdev->dev, "Unknown event %d from PF\n", + vpe->event); break; } return; } if (v_retval) { - dev_err(&adapter->pdev->dev, "%s: PF returned error %d (%s) to our request %d\n", - __func__, v_retval, - i40evf_stat_str(&adapter->hw, v_retval), v_opcode); + dev_err(&adapter->pdev->dev, "PF returned error %d (%s) to our request %d\n", + v_retval, i40evf_stat_str(&adapter->hw, v_retval), + v_opcode); } switch (v_opcode) { case I40E_VIRTCHNL_OP_GET_STATS: { -- cgit v0.10.2 From dd38c583aea07c508caeaf979c64e267584ee067 Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Wed, 26 Aug 2015 15:14:18 -0400 Subject: i40e: add little endian conversion for checksum The checksum is not correct on big endian machines so add code to swap it correctly. Change-ID: Ic92b886d172a2cbe49f5d7eee1bc78e447023c7b Signed-off-by: Jesse Brandeburg Signed-off-by: Paul M Stillwell Jr Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_nvm.c b/drivers/net/ethernet/intel/i40e/i40e_nvm.c index d0288ad..2142e10 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_nvm.c +++ b/drivers/net/ethernet/intel/i40e/i40e_nvm.c @@ -547,11 +547,13 @@ i40e_status i40e_update_nvm_checksum(struct i40e_hw *hw) { i40e_status ret_code = 0; u16 checksum; + __le16 le_sum; ret_code = i40e_calc_nvm_checksum(hw, &checksum); + le_sum = cpu_to_le16(checksum); if (!ret_code) ret_code = i40e_write_nvm_aq(hw, 0x00, I40E_SR_SW_CHECKSUM_WORD, - 1, &checksum, true); + 1, &le_sum, true); return ret_code; } -- cgit v0.10.2 From a72a5abcb37beac163704efba6a3d33ebca4d90a Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Wed, 26 Aug 2015 15:14:19 -0400 Subject: i40e: fix bug in return from get_link_status and avoid spurious link messages Previously, the driver could call this function and have only true/false returned, but false could mean multiple things like failure to read or link was down. This change allows the caller to get all return values in the call chain bubbled back to the source, which keeps information about failures from being lost. Also, in some unlikely scenarios, the firmware can become slow to respond to admin queue (AQ) queries for link state. Should the AQ time out, the driver can detect the state and avoid a link change when there may have been none. Change-ID: Ib2ac38407b7880750fb891b392fa77457fe6c21c Signed-off-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 7a70abc..2839ea5 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -2235,27 +2235,28 @@ i40e_status i40e_aq_send_driver_version(struct i40e_hw *hw, /** * i40e_get_link_status - get status of the HW network link * @hw: pointer to the hw struct + * @link_up: pointer to bool (true/false = linkup/linkdown) * - * Returns true if link is up, false if link is down. + * Variable link_up true if link is up, false if link is down. + * The variable link_up is invalid if returned value of status != 0 * * Side effect: LinkStatusEvent reporting becomes enabled **/ -bool i40e_get_link_status(struct i40e_hw *hw) +i40e_status i40e_get_link_status(struct i40e_hw *hw, bool *link_up) { i40e_status status = 0; - bool link_status = false; if (hw->phy.get_link_info) { status = i40e_aq_get_link_info(hw, true, NULL, NULL); if (status) - goto i40e_get_link_status_exit; + i40e_debug(hw, I40E_DEBUG_LINK, "get link failed: status %d\n", + status); } - link_status = hw->phy.link_info.link_info & I40E_AQ_LINK_UP; + *link_up = hw->phy.link_info.link_info & I40E_AQ_LINK_UP; -i40e_get_link_status_exit: - return link_status; + return status; } /** diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 930369c..1fa38f6 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -1516,9 +1516,18 @@ static int i40e_link_test(struct net_device *netdev, u64 *data) { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_pf *pf = np->vsi->back; + i40e_status status; + bool link_up = false; netif_info(pf, hw, netdev, "link test\n"); - if (i40e_get_link_status(&pf->hw)) + status = i40e_get_link_status(&pf->hw, &link_up); + if (status) { + netif_err(pf, drv, netdev, "link query timed out, please retry test\n"); + *data = 1; + return *data; + } + + if (link_up) *data = 0; else *data = 1; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 1fa1eba..f205e18 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -5836,15 +5836,23 @@ static void i40e_veb_link_event(struct i40e_veb *veb, bool link_up) **/ static void i40e_link_event(struct i40e_pf *pf) { - bool new_link, old_link; struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; u8 new_link_speed, old_link_speed; + i40e_status status; + bool new_link, old_link; /* set this to force the get_link_status call to refresh state */ pf->hw.phy.get_link_info = true; old_link = (pf->hw.phy.link_info_old.link_info & I40E_AQ_LINK_UP); - new_link = i40e_get_link_status(&pf->hw); + + status = i40e_get_link_status(&pf->hw, &new_link); + if (status) { + dev_dbg(&pf->pdev->dev, "couldn't get link state, status: %d\n", + status); + return; + } + old_link_speed = pf->hw.phy.link_info_old.link_speed; new_link_speed = pf->hw.phy.link_info.link_speed; diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h index dcb72a8..e51e156 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h +++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h @@ -258,7 +258,7 @@ i40e_status i40e_init_shared_code(struct i40e_hw *hw); i40e_status i40e_pf_reset(struct i40e_hw *hw); void i40e_clear_hw(struct i40e_hw *hw); void i40e_clear_pxe_mode(struct i40e_hw *hw); -bool i40e_get_link_status(struct i40e_hw *hw); +i40e_status i40e_get_link_status(struct i40e_hw *hw, bool *link_up); i40e_status i40e_get_mac_addr(struct i40e_hw *hw, u8 *mac_addr); i40e_status i40e_read_bw_from_alt_ram(struct i40e_hw *hw, u32 *max_bw, u32 *min_bw, bool *min_valid, -- cgit v0.10.2 From 2f41f3358672dfda67c1e254f1e823d98e6a3099 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Wed, 26 Aug 2015 15:14:20 -0400 Subject: i40e/i40evf: fix unicast mac address add When using something like "ip maddr add ..." to add another unicast mac address to the netdev, the mac address comes into the set_rx_mode handler in the multicast list whether it is a unicast or multicast address. This was confusing the code when it was trying to search for addresses that needed to be deleted from the VSI, because it was looking for the VSI unicast address in the netdev unicast list. The result was that a new unicast address would get added to the VSI list and then immediately removed, and would never actually make it down into the hardware. This patch removes the separation from unicast and multicast in the search for filters to be deleted. It also simplifies the logic a little with a jump to the bottom of the loop when an address is found. Now it doesn't matter which netdev list the address is hiding in, we'll check them all. Change-ID: Ie3685a92427ae7d2212bf948919ce295bc7a874c Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index f205e18..84f9dd9 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1726,36 +1726,27 @@ static void i40e_set_rx_mode(struct net_device *netdev) /* remove filter if not in netdev list */ list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) { - bool found = false; if (!f->is_netdev) continue; - if (is_multicast_ether_addr(f->macaddr)) { - netdev_for_each_mc_addr(mca, netdev) { - if (ether_addr_equal(mca->addr, f->macaddr)) { - found = true; - break; - } - } - } else { - netdev_for_each_uc_addr(uca, netdev) { - if (ether_addr_equal(uca->addr, f->macaddr)) { - found = true; - break; - } - } + netdev_for_each_mc_addr(mca, netdev) + if (ether_addr_equal(mca->addr, f->macaddr)) + goto bottom_of_search_loop; - for_each_dev_addr(netdev, ha) { - if (ether_addr_equal(ha->addr, f->macaddr)) { - found = true; - break; - } - } - } - if (!found) - i40e_del_filter( - vsi, f->macaddr, I40E_VLAN_ANY, false, true); + 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, false, true); + +bottom_of_search_loop: + continue; } /* check for other flag changes */ diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index f3a61c4..cc78fdf 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -855,6 +855,7 @@ static void i40evf_set_rx_mode(struct net_device *netdev) struct i40evf_mac_filter *f, *ftmp; struct netdev_hw_addr *uca; struct netdev_hw_addr *mca; + struct netdev_hw_addr *ha; int count = 50; /* add addr if not already in the filter list */ @@ -876,29 +877,27 @@ static void i40evf_set_rx_mode(struct net_device *netdev) } /* remove filter if not in netdev list */ list_for_each_entry_safe(f, ftmp, &adapter->mac_filter_list, list) { - bool found = false; - - if (is_multicast_ether_addr(f->macaddr)) { - netdev_for_each_mc_addr(mca, netdev) { - if (ether_addr_equal(mca->addr, f->macaddr)) { - found = true; - break; - } - } - } else { - netdev_for_each_uc_addr(uca, netdev) { - if (ether_addr_equal(uca->addr, f->macaddr)) { - found = true; - break; - } - } - if (ether_addr_equal(f->macaddr, adapter->hw.mac.addr)) - found = true; - } - if (!found) { - f->remove = true; - adapter->aq_required |= I40EVF_FLAG_AQ_DEL_MAC_FILTER; - } + 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; + + if (ether_addr_equal(f->macaddr, adapter->hw.mac.addr)) + goto bottom_of_search_loop; + + /* f->macaddr wasn't found in uc, mc, or ha list so delete it */ + f->remove = true; + adapter->aq_required |= I40EVF_FLAG_AQ_DEL_MAC_FILTER; + +bottom_of_search_loop: + continue; } clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section); } -- cgit v0.10.2 From 2fc3d7152ae9562c15c30ed4a766ba05a3db8200 Mon Sep 17 00:00:00 2001 From: Anjali Singhai Jain Date: Thu, 27 Aug 2015 11:42:29 -0400 Subject: i40e/i40evf: Add a stat to keep track of linearization count Keep track of how many times we ask the stack to linearize the skb because the HW cannot handle skbs with more than 8 frags per segment/single packet. Change-ID: If455452060963a769bbe6112cba952e79e944b52 Signed-off-by: Anjali Singhai Jain Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index a54577a..681bd5d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -475,6 +475,7 @@ struct i40e_vsi { #endif u32 tx_restart; u32 tx_busy; + u64 tx_linearize; u32 rx_buf_failed; u32 rx_page_failed; diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 1fa38f6..ffa9431 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -87,6 +87,7 @@ static const struct i40e_stats i40e_gstrings_misc_stats[] = { I40E_VSI_STAT("rx_broadcast", eth_stats.rx_broadcast), I40E_VSI_STAT("tx_broadcast", eth_stats.tx_broadcast), I40E_VSI_STAT("rx_unknown_protocol", eth_stats.rx_unknown_protocol), + I40E_VSI_STAT("tx_linearize", tx_linearize), }; static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 84f9dd9..fb4b34d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -879,6 +879,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) u32 rx_page, rx_buf; u64 bytes, packets; unsigned int start; + u64 tx_linearize; u64 rx_p, rx_b; u64 tx_p, tx_b; u16 q; @@ -897,7 +898,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) */ rx_b = rx_p = 0; tx_b = tx_p = 0; - tx_restart = tx_busy = 0; + tx_restart = tx_busy = tx_linearize = 0; rx_page = 0; rx_buf = 0; rcu_read_lock(); @@ -914,6 +915,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) tx_p += packets; tx_restart += p->tx_stats.restart_queue; tx_busy += p->tx_stats.tx_busy; + tx_linearize += p->tx_stats.tx_linearize; /* Rx queue is part of the same block as Tx queue */ p = &p[1]; @@ -930,6 +932,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) rcu_read_unlock(); vsi->tx_restart = tx_restart; vsi->tx_busy = tx_busy; + vsi->tx_linearize = tx_linearize; vsi->rx_page_failed = rx_page; vsi->rx_buf_failed = rx_buf; diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 85e61b0..889ed10 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -2771,10 +2771,11 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, if (tsyn) tx_flags |= I40E_TX_FLAGS_TSYN; - if (i40e_chk_linearize(skb, tx_flags)) + if (i40e_chk_linearize(skb, tx_flags)) { if (skb_linearize(skb)) goto out_drop; - + tx_ring->tx_stats.tx_linearize++; + } skb_tx_timestamp(skb); /* always enable CRC insertion offload */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index 7c9975c..ac3fb3a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -188,6 +188,7 @@ struct i40e_tx_queue_stats { u64 restart_queue; u64 tx_busy; u64 tx_done_old; + u64 tx_linearize; }; struct i40e_rx_queue_stats { diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 0e1a4d6..3b102f2 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -1927,10 +1927,11 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, else if (tso) tx_flags |= I40E_TX_FLAGS_TSO; - if (i40e_chk_linearize(skb, tx_flags)) + if (i40e_chk_linearize(skb, tx_flags)) { if (skb_linearize(skb)) goto out_drop; - + tx_ring->tx_stats.tx_linearize++; + } skb_tx_timestamp(skb); /* always enable CRC insertion offload */ diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index d5cb7ac..93b90f7 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -187,6 +187,7 @@ struct i40e_tx_queue_stats { u64 restart_queue; u64 tx_busy; u64 tx_done_old; + u64 tx_linearize; }; struct i40e_rx_queue_stats { -- cgit v0.10.2 From 2642f02528b6e9c1e904f6e3ec902d8a2deb7af5 Mon Sep 17 00:00:00 2001 From: Neerav Parikh Date: Thu, 27 Aug 2015 11:42:30 -0400 Subject: i40e: Additional checks for CEE APP priority validity The firmware has added additional status information to allow software to determine if the APP priority for FCoE/iSCSI/FIP is valid or not in CEE DCBX mode. This patch adds to support those additional checks and will only add applications to the software table that have oper and sync bits set without any error. Change-ID: I0a76c52427dadf97d4dba4538a3068d05e4eb56b Signed-off-by: Neerav Parikh Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb.c b/drivers/net/ethernet/intel/i40e/i40e_dcb.c index 90de46a..9aee35d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb.c +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb.c @@ -531,37 +531,55 @@ static void i40e_cee_to_dcb_config( dcbcfg->pfc.pfcenable = cee_cfg->oper_pfc_en; dcbcfg->pfc.pfccap = I40E_MAX_TRAFFIC_CLASS; - status = (tlv_status & I40E_AQC_CEE_APP_STATUS_MASK) >> - I40E_AQC_CEE_APP_STATUS_SHIFT; + i = 0; + status = (tlv_status & I40E_AQC_CEE_FCOE_STATUS_MASK) >> + I40E_AQC_CEE_FCOE_STATUS_SHIFT; err = (status & I40E_TLV_STATUS_ERR) ? 1 : 0; sync = (status & I40E_TLV_STATUS_SYNC) ? 1 : 0; oper = (status & I40E_TLV_STATUS_OPER) ? 1 : 0; - /* Add APPs if Error is False and Oper/Sync is True */ + /* Add FCoE APP if Error is False and Oper/Sync is True */ if (!err && sync && oper) { - /* CEE operating configuration supports FCoE/iSCSI/FIP only */ - dcbcfg->numapps = I40E_CEE_OPER_MAX_APPS; - /* FCoE APP */ - dcbcfg->app[0].priority = + dcbcfg->app[i].priority = (app_prio & I40E_AQC_CEE_APP_FCOE_MASK) >> I40E_AQC_CEE_APP_FCOE_SHIFT; - dcbcfg->app[0].selector = I40E_APP_SEL_ETHTYPE; - dcbcfg->app[0].protocolid = I40E_APP_PROTOID_FCOE; + dcbcfg->app[i].selector = I40E_APP_SEL_ETHTYPE; + dcbcfg->app[i].protocolid = I40E_APP_PROTOID_FCOE; + i++; + } + status = (tlv_status & I40E_AQC_CEE_ISCSI_STATUS_MASK) >> + I40E_AQC_CEE_ISCSI_STATUS_SHIFT; + err = (status & I40E_TLV_STATUS_ERR) ? 1 : 0; + sync = (status & I40E_TLV_STATUS_SYNC) ? 1 : 0; + oper = (status & I40E_TLV_STATUS_OPER) ? 1 : 0; + /* Add iSCSI APP if Error is False and Oper/Sync is True */ + if (!err && sync && oper) { /* iSCSI APP */ - dcbcfg->app[1].priority = + dcbcfg->app[i].priority = (app_prio & I40E_AQC_CEE_APP_ISCSI_MASK) >> I40E_AQC_CEE_APP_ISCSI_SHIFT; - dcbcfg->app[1].selector = I40E_APP_SEL_TCPIP; - dcbcfg->app[1].protocolid = I40E_APP_PROTOID_ISCSI; + dcbcfg->app[i].selector = I40E_APP_SEL_TCPIP; + dcbcfg->app[i].protocolid = I40E_APP_PROTOID_ISCSI; + i++; + } + status = (tlv_status & I40E_AQC_CEE_FIP_STATUS_MASK) >> + I40E_AQC_CEE_FIP_STATUS_SHIFT; + err = (status & I40E_TLV_STATUS_ERR) ? 1 : 0; + sync = (status & I40E_TLV_STATUS_SYNC) ? 1 : 0; + oper = (status & I40E_TLV_STATUS_OPER) ? 1 : 0; + /* Add FIP APP if Error is False and Oper/Sync is True */ + if (!err && sync && oper) { /* FIP APP */ - dcbcfg->app[2].priority = + dcbcfg->app[i].priority = (app_prio & I40E_AQC_CEE_APP_FIP_MASK) >> I40E_AQC_CEE_APP_FIP_SHIFT; - dcbcfg->app[2].selector = I40E_APP_SEL_ETHTYPE; - dcbcfg->app[2].protocolid = I40E_APP_PROTOID_FIP; + dcbcfg->app[i].selector = I40E_APP_SEL_ETHTYPE; + dcbcfg->app[i].protocolid = I40E_APP_PROTOID_FIP; + i++; } + dcbcfg->numapps = i; } /** -- cgit v0.10.2 From 313ed2d52052ad8dab9a6182cde55a832873cbea Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Thu, 27 Aug 2015 11:42:31 -0400 Subject: i40evf: propagate interrupt allocation failure Lower level functions are properly reporting errors, and higher-level functions are correctly responding to errors, but the errors aren't actually getting through. Typically, the middle-manager function seems to want to shield its boss from any bad news. This change fixes a panic if the driver is unable to enable MSI-X or is unable to acquire enough vectors. Change-ID: Ifd5787ce92519a5d97e4b465902db930d97b71a1 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index cc78fdf..0d18446 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -1163,7 +1163,7 @@ static int i40evf_set_interrupt_capability(struct i40evf_adapter *adapter) for (vector = 0; vector < v_budget; vector++) adapter->msix_entries[vector].entry = vector; - i40evf_acquire_msix_vectors(adapter, v_budget); + err = i40evf_acquire_msix_vectors(adapter, v_budget); out: adapter->netdev->real_num_tx_queues = pairs; -- cgit v0.10.2 From 7fd0ac66c2de5767e234fea6436d28dce210da80 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Thu, 27 Aug 2015 11:42:32 -0400 Subject: i40e/i40evf: remove unused opcode This opcode is not required. VFs that program RSS through the firmware do it by interacting directly with the firmware, and do not need to use the virtual channel for this functionality. Change-ID: Iaf17d2600e28ff1b6be8653f2fe9df1facd23b0e Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h index 0f8d415..95d0f8c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h @@ -81,7 +81,6 @@ enum i40e_virtchnl_ops { I40E_VIRTCHNL_OP_GET_STATS = 15, I40E_VIRTCHNL_OP_FCOE = 16, I40E_VIRTCHNL_OP_EVENT = 17, - I40E_VIRTCHNL_OP_CONFIG_RSS = 18, }; /* Virtual channel message descriptor. This overlays the admin queue diff --git a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h index e6db20e..cadda64 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h @@ -81,7 +81,6 @@ enum i40e_virtchnl_ops { I40E_VIRTCHNL_OP_GET_STATS = 15, I40E_VIRTCHNL_OP_FCOE = 16, I40E_VIRTCHNL_OP_EVENT = 17, - I40E_VIRTCHNL_OP_CONFIG_RSS = 18, }; /* Virtual channel message descriptor. This overlays the admin queue -- cgit v0.10.2 From 6c566dd5a1253f73458ce6ba6cf3830e9d38c132 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 7 Oct 2015 15:32:13 +0200 Subject: Bluetooth: Send index information updates to monitor channel The Bluetooth public device address might change during controller setup and it makes it a lot simpler for monitoring tools if they just get told what the new address is. In addition include the manufacturer / company information of the controller. That allows for easy vendor specific HCI command and event handling. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/hci_mon.h b/include/net/bluetooth/hci_mon.h index 37e4283..842bb75 100644 --- a/include/net/bluetooth/hci_mon.h +++ b/include/net/bluetooth/hci_mon.h @@ -41,6 +41,7 @@ struct hci_mon_hdr { #define HCI_MON_SCO_RX_PKT 7 #define HCI_MON_OPEN_INDEX 8 #define HCI_MON_CLOSE_INDEX 9 +#define HCI_MON_INDEX_INFO 10 struct hci_mon_new_index { __u8 type; @@ -50,4 +51,10 @@ struct hci_mon_new_index { } __packed; #define HCI_MON_NEW_INDEX_SIZE 16 +struct hci_mon_index_info { + bdaddr_t bdaddr; + __le16 manufacturer; +} __packed; +#define HCI_MON_INDEX_INFO_SIZE 8 + #endif /* __HCI_MON_H */ diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 64ebe84..9bf30db 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -303,6 +303,7 @@ static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event) { struct hci_mon_hdr *hdr; struct hci_mon_new_index *ni; + struct hci_mon_index_info *ii; struct sk_buff *skb; __le16 opcode; @@ -312,7 +313,7 @@ static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event) if (!skb) return NULL; - ni = (void *) skb_put(skb, HCI_MON_NEW_INDEX_SIZE); + ni = (void *)skb_put(skb, HCI_MON_NEW_INDEX_SIZE); ni->type = hdev->dev_type; ni->bus = hdev->bus; bacpy(&ni->bdaddr, &hdev->bdaddr); @@ -329,6 +330,18 @@ static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event) opcode = cpu_to_le16(HCI_MON_DEL_INDEX); break; + case HCI_DEV_UP: + skb = bt_skb_alloc(HCI_MON_INDEX_INFO_SIZE, GFP_ATOMIC); + if (!skb) + return NULL; + + ii = (void *)skb_put(skb, HCI_MON_INDEX_INFO_SIZE); + bacpy(&ii->bdaddr, &hdev->bdaddr); + ii->manufacturer = cpu_to_le16(hdev->manufacturer); + + opcode = cpu_to_le16(HCI_MON_INDEX_INFO); + break; + case HCI_DEV_OPEN: skb = bt_skb_alloc(0, GFP_ATOMIC); if (!skb) @@ -384,6 +397,16 @@ static void send_monitor_replay(struct sock *sk) if (sock_queue_rcv_skb(sk, skb)) kfree_skb(skb); + + if (!test_bit(HCI_UP, &hdev->flags)) + continue; + + skb = create_monitor_event(hdev, HCI_DEV_UP); + if (!skb) + continue; + + if (sock_queue_rcv_skb(sk, skb)) + kfree_skb(skb); } read_unlock(&hci_dev_list_lock); -- cgit v0.10.2 From e875ff84079b9e7d3ce24b97e3396230d41044d4 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 7 Oct 2015 16:38:35 +0200 Subject: Bluetooth: Add support for vendor specific diagnostic channel Introduce hci_recv_diag function for HCI drivers to allow sending vendor specific diagnostic messages into the Bluetooth core stack. The messages are not processed, but they are forwarded to the monitor channel and can be retrieved by user space diagnostic tools. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index e7f938c..cf75c43 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -262,6 +262,7 @@ enum { #define HCI_ACLDATA_PKT 0x02 #define HCI_SCODATA_PKT 0x03 #define HCI_EVENT_PKT 0x04 +#define HCI_DIAG_PKT 0xf0 #define HCI_VENDOR_PKT 0xff /* HCI packet types */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 61dc786..d473b67 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1066,6 +1066,7 @@ int hci_remove_adv_instance(struct hci_dev *hdev, u8 instance); void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb); int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb); +int hci_recv_diag(struct hci_dev *hdev, struct sk_buff *skb); void hci_init_sysfs(struct hci_dev *hdev); void hci_conn_init_sysfs(struct hci_conn *conn); diff --git a/include/net/bluetooth/hci_mon.h b/include/net/bluetooth/hci_mon.h index 842bb75..2b67567 100644 --- a/include/net/bluetooth/hci_mon.h +++ b/include/net/bluetooth/hci_mon.h @@ -42,6 +42,7 @@ struct hci_mon_hdr { #define HCI_MON_OPEN_INDEX 8 #define HCI_MON_CLOSE_INDEX 9 #define HCI_MON_INDEX_INFO 10 +#define HCI_MON_VENDOR_DIAG 11 struct hci_mon_new_index { __u8 type; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 40a6701..8193845 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3493,6 +3493,21 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb) } EXPORT_SYMBOL(hci_recv_frame); +/* Receive diagnostic message from HCI drivers */ +int hci_recv_diag(struct hci_dev *hdev, struct sk_buff *skb) +{ + /* Time stamp */ + __net_timestamp(skb); + + /* Mark as diagnostic packet and send to monitor */ + bt_cb(skb)->pkt_type = HCI_DIAG_PKT; + hci_send_to_monitor(hdev, skb); + + kfree_skb(skb); + return 0; +} +EXPORT_SYMBOL(hci_recv_diag); + /* ---- Interface to upper protocols ---- */ int hci_register_cb(struct hci_cb *cb) diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 9bf30db..9a100c1 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -279,6 +279,9 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb) else opcode = cpu_to_le16(HCI_MON_SCO_TX_PKT); break; + case HCI_DIAG_PKT: + opcode = cpu_to_le16(HCI_MON_VENDOR_DIAG); + break; default: return; } -- cgit v0.10.2 From 94c58132c062855df53db9a46fb8da0b878d69f0 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 7 Oct 2015 19:12:54 +0200 Subject: Bluetooth: hci_bcm: Enable parsing of LM_DIAG messages The Broadcom UART based controllers can send LM_DIAG messages with the identifier 0x07 inside the HCI stream. These messages are 63 octets in size and have no variable payload or length indicator. This patch adds correct parsing information for the h4_recv_buf handler and in case these packets are received, they are forwarded to the Bluetooth core via hci_recv_diag interface. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 5128732..1425bf5 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -41,6 +41,9 @@ #include "btbcm.h" #include "hci_uart.h" +#define BCM_LM_DIAG_PKT 0x07 +#define BCM_LM_DIAG_SIZE 63 + #define BCM_AUTOSUSPEND_DELAY 5000 /* default autosleep delay */ struct bcm_device { @@ -396,10 +399,18 @@ finalize: return err; } +#define BCM_RECV_LM_DIAG \ + .type = BCM_LM_DIAG_PKT, \ + .hlen = BCM_LM_DIAG_SIZE, \ + .loff = 0, \ + .lsize = 0, \ + .maxlen = BCM_LM_DIAG_SIZE + static const struct h4_recv_pkt bcm_recv_pkts[] = { - { H4_RECV_ACL, .recv = hci_recv_frame }, - { H4_RECV_SCO, .recv = hci_recv_frame }, - { H4_RECV_EVENT, .recv = hci_recv_frame }, + { H4_RECV_ACL, .recv = hci_recv_frame }, + { H4_RECV_SCO, .recv = hci_recv_frame }, + { H4_RECV_EVENT, .recv = hci_recv_frame }, + { BCM_RECV_LM_DIAG, .recv = hci_recv_diag }, }; static int bcm_recv(struct hci_uart *hu, const void *data, int count) -- cgit v0.10.2 From 4b4113d6dbdbdac095743c05f694af9b7cdc9a44 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 7 Oct 2015 19:52:35 +0200 Subject: Bluetooth: Add debugfs entry for setting vendor diagnostic mode This adds a new debugfs entry for enabling and disabling the vendor diagnostic mode. It is only exposed for drivers that provide the set_diag driver callback and actually have an option for vendor specific diagnostic information. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index cf75c43..a26ff28 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -240,6 +240,7 @@ enum { HCI_LE_SCAN_INTERRUPTED, HCI_DUT_MODE, + HCI_VENDOR_DIAG, HCI_FORCE_BREDR_SMP, HCI_FORCE_STATIC_ADDR, diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index d473b67..f28470e 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -398,6 +398,7 @@ struct hci_dev { int (*send)(struct hci_dev *hdev, struct sk_buff *skb); void (*notify)(struct hci_dev *hdev, unsigned int evt); void (*hw_error)(struct hci_dev *hdev, u8 code); + int (*set_diag)(struct hci_dev *hdev, bool enable); int (*set_bdaddr)(struct hci_dev *hdev, const bdaddr_t *bdaddr); }; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 8193845..e75bc54 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -134,6 +134,56 @@ static const struct file_operations dut_mode_fops = { .llseek = default_llseek, }; +static ssize_t vendor_diag_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hci_dev *hdev = file->private_data; + char buf[3]; + + buf[0] = hci_dev_test_flag(hdev, HCI_VENDOR_DIAG) ? 'Y': 'N'; + buf[1] = '\n'; + buf[2] = '\0'; + return simple_read_from_buffer(user_buf, count, ppos, buf, 2); +} + +static ssize_t vendor_diag_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hci_dev *hdev = file->private_data; + char buf[32]; + size_t buf_size = min(count, (sizeof(buf)-1)); + bool enable; + int err; + + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + buf[buf_size] = '\0'; + if (strtobool(buf, &enable)) + return -EINVAL; + + hci_req_lock(hdev); + err = hdev->set_diag(hdev, enable); + hci_req_unlock(hdev); + + if (err < 0) + return err; + + if (enable) + hci_dev_set_flag(hdev, HCI_VENDOR_DIAG); + else + hci_dev_clear_flag(hdev, HCI_VENDOR_DIAG); + + return count; +} + +static const struct file_operations vendor_diag_fops = { + .open = simple_open, + .read = vendor_diag_read, + .write = vendor_diag_write, + .llseek = default_llseek, +}; + /* ---- HCI requests ---- */ static void hci_req_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode, @@ -850,12 +900,19 @@ static int __hci_init(struct hci_dev *hdev) if (err < 0) return err; - /* The Device Under Test (DUT) mode is special and available for - * all controller types. So just create it early on. - */ if (hci_dev_test_flag(hdev, HCI_SETUP)) { + /* The Device Under Test (DUT) mode is special and available + * for all controller types. So just create it early on. + */ debugfs_create_file("dut_mode", 0644, hdev->debugfs, hdev, &dut_mode_fops); + + /* When the driver supports the set_diag callback, then + * expose an entry to modify the vendor diagnostic setting. + */ + if (hdev->set_diag) + debugfs_create_file("vendor_diag", 0644, hdev->debugfs, + hdev, &vendor_diag_fops); } err = __hci_req_sync(hdev, hci_init2_req, 0, HCI_INIT_TIMEOUT); -- cgit v0.10.2 From 075e1f5e6c201111e32da99919eb052edc6d1c82 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 7 Oct 2015 20:08:26 +0200 Subject: Bluetooth: hci_bcm: Enable support for set_diag driver callback The set_diag driver callback allows enabling and disabling the vendor specific diagnostic information. Since Broadcom chips have support for a dedicated LM_DIAG channel, hook it up accordingly. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 1425bf5..645e66e 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -249,6 +249,29 @@ static inline int bcm_request_irq(struct bcm_data *bcm) { return 0; } static inline int bcm_setup_sleep(struct hci_uart *hu) { return 0; } #endif +static int bcm_set_diag(struct hci_dev *hdev, bool enable) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + struct bcm_data *bcm = hu->priv; + struct sk_buff *skb; + + if (!test_bit(HCI_RUNNING, &hdev->flags)) + return -ENETDOWN; + + skb = bt_skb_alloc(3, GFP_KERNEL); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + *skb_put(skb, 1) = BCM_LM_DIAG_PKT; + *skb_put(skb, 1) = 0xf0; + *skb_put(skb, 1) = enable; + + skb_queue_tail(&bcm->txq, skb); + hci_uart_tx_wakeup(hu); + + return 0; +} + static int bcm_open(struct hci_uart *hu) { struct bcm_data *bcm; @@ -342,6 +365,7 @@ static int bcm_setup(struct hci_uart *hu) bt_dev_dbg(hu->hdev, "hu %p", hu); + hu->hdev->set_diag = bcm_set_diag; hu->hdev->set_bdaddr = btbcm_set_bdaddr; err = btbcm_initialize(hu->hdev, fw_name, sizeof(fw_name)); -- cgit v0.10.2 From acc649c6540ef224cc07d17c4b632da9dedfb6a2 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 8 Oct 2015 01:53:55 +0200 Subject: Bluetooth: Fix interaction of HCI_QUIRK_RESET_ON_CLOSE and HCI_AUTO_OFF When the controller requires the HCI Reset command to be send when closing the transport, the HCI_AUTO_OFF needs to be accounted for. The current code tries to actually do that, but the flag gets cleared to early. So store its value and use it that stored value instead of checking for a flag that is always cleared. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index e75bc54..43a1f2d 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1614,6 +1614,8 @@ static void hci_pend_le_actions_clear(struct hci_dev *hdev) int hci_dev_do_close(struct hci_dev *hdev) { + bool auto_off; + BT_DBG("%s %p", hdev->name, hdev); if (!hci_dev_test_flag(hdev, HCI_UNREGISTER) && @@ -1669,10 +1671,10 @@ int hci_dev_do_close(struct hci_dev *hdev) hci_discovery_set_state(hdev, DISCOVERY_STOPPED); - if (!hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF)) { - if (hdev->dev_type == HCI_BREDR) - mgmt_powered(hdev, 0); - } + auto_off = hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF); + + if (!auto_off && hdev->dev_type == HCI_BREDR) + mgmt_powered(hdev, 0); hci_inquiry_cache_flush(hdev); hci_pend_le_actions_clear(hdev); @@ -1689,9 +1691,8 @@ int hci_dev_do_close(struct hci_dev *hdev) /* Reset device */ skb_queue_purge(&hdev->cmd_q); atomic_set(&hdev->cmd_cnt, 1); - if (!hci_dev_test_flag(hdev, HCI_AUTO_OFF) && - !hci_dev_test_flag(hdev, HCI_UNCONFIGURED) && - test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks)) { + if (test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks) && + !auto_off && !hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) { set_bit(HCI_INIT, &hdev->flags); __hci_req_sync(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT); clear_bit(HCI_INIT, &hdev->flags); -- cgit v0.10.2 From ddd68ec8f4847b460c9f580076eafe13b031a6fd Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 8 Oct 2015 02:24:06 +0200 Subject: Bluetooth: bpa10x: Read revision information in setup stage For debugging pruposes, read the revision string of the BPA-10x devices and print it. For example one of the latest devices respond with the string SNIF_102,BB930,02/01/18,10:37:56. < HCI Command: Vendor (0x3f|0x000e) plen 1 07 . > HCI Event: Command Complete (0x0e) plen 49 Vendor (0x3f|0x000e) ncmd 1 Status: Success (0x00) 53 4e 49 46 5f 31 30 32 2c 42 42 39 33 30 2c 30 SNIF_102,BB930,0 32 2f 30 31 2f 31 38 2c 31 30 3a 33 37 3a 35 36 2/01/18,10:37:56 00 00 00 00 00 00 00 00 00 00 00 00 00 ............. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c index 88e004e..618b1ef 100644 --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -342,6 +342,24 @@ static int bpa10x_flush(struct hci_dev *hdev) return 0; } +static int bpa10x_setup(struct hci_dev *hdev) +{ + const u8 req[] = { 0x07 }; + struct sk_buff *skb; + + BT_DBG("%s", hdev->name); + + /* Read revision string */ + skb = __hci_cmd_sync(hdev, 0xfc0e, sizeof(req), req, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1)); + + kfree_skb(skb); + return 0; +} + static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { struct bpa10x_data *data = hci_get_drvdata(hdev); @@ -454,6 +472,7 @@ static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id * hdev->open = bpa10x_open; hdev->close = bpa10x_close; hdev->flush = bpa10x_flush; + hdev->setup = bpa10x_setup; hdev->send = bpa10x_send_frame; set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); -- cgit v0.10.2 From 881f7e86a1e0322fbda42c92a36b13c44120bca2 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 8 Oct 2015 03:01:44 +0200 Subject: Bluetooth: bpa10x: Add support for set_diag driver callback The BPA-10x devices support tracing operation. Use the set_diag driver callback to allow enabling and disabling that functionality. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c index 618b1ef..a1a0f80 100644 --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -438,6 +438,25 @@ static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb) return 0; } +static int bpa10x_set_diag(struct hci_dev *hdev, bool enable) +{ + const u8 req[] = { 0x00, enable }; + struct sk_buff *skb; + + BT_DBG("%s", hdev->name); + + if (!test_bit(HCI_RUNNING, &hdev->flags)) + return -ENETDOWN; + + /* Enable sniffer operation */ + skb = __hci_cmd_sync(hdev, 0xfc0e, sizeof(req), req, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + kfree_skb(skb); + return 0; +} + static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct bpa10x_data *data; @@ -474,6 +493,7 @@ static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id * hdev->flush = bpa10x_flush; hdev->setup = bpa10x_setup; hdev->send = bpa10x_send_frame; + hdev->set_diag = bpa10x_set_diag; set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); -- cgit v0.10.2 From 943cc592195ea458058c121d9c4d7481d0f28be1 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 8 Oct 2015 03:06:53 +0200 Subject: Bluetooth: bpa10x: Use h4_recv_buf helper for frame reassembly The manually coded frame reassembly is actually broken. The h4_recv_buf helper from the UART driver is a perfect fit for frame reassembly for this driver. So just export that function and use it here as well. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 6299954..c9c5dd0 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -184,6 +184,7 @@ config BT_HCIBCM203X config BT_HCIBPA10X tristate "HCI BPA10x USB driver" depends on USB + select BT_HCIUART_H4 help Bluetooth HCI BPA10x USB driver. This driver provides support for the Digianswer BPA 100/105 Bluetooth diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c index a1a0f80..49c397e2 100644 --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -35,7 +35,9 @@ #include #include -#define VERSION "0.10" +#include "hci_uart.h" + +#define VERSION "0.11" static const struct usb_device_id bpa10x_table[] = { /* Tektronix BPA 100/105 (Digianswer) */ @@ -56,112 +58,6 @@ struct bpa10x_data { struct sk_buff *rx_skb[2]; }; -#define HCI_VENDOR_HDR_SIZE 5 - -struct hci_vendor_hdr { - __u8 type; - __le16 snum; - __le16 dlen; -} __packed; - -static int bpa10x_recv(struct hci_dev *hdev, int queue, void *buf, int count) -{ - struct bpa10x_data *data = hci_get_drvdata(hdev); - - BT_DBG("%s queue %d buffer %p count %d", hdev->name, - queue, buf, count); - - if (queue < 0 || queue > 1) - return -EILSEQ; - - hdev->stat.byte_rx += count; - - while (count) { - struct sk_buff *skb = data->rx_skb[queue]; - struct { __u8 type; int expect; } *scb; - int type, len = 0; - - if (!skb) { - /* Start of the frame */ - - type = *((__u8 *) buf); - count--; buf++; - - switch (type) { - case HCI_EVENT_PKT: - if (count >= HCI_EVENT_HDR_SIZE) { - struct hci_event_hdr *h = buf; - len = HCI_EVENT_HDR_SIZE + h->plen; - } else - return -EILSEQ; - break; - - case HCI_ACLDATA_PKT: - if (count >= HCI_ACL_HDR_SIZE) { - struct hci_acl_hdr *h = buf; - len = HCI_ACL_HDR_SIZE + - __le16_to_cpu(h->dlen); - } else - return -EILSEQ; - break; - - case HCI_SCODATA_PKT: - if (count >= HCI_SCO_HDR_SIZE) { - struct hci_sco_hdr *h = buf; - len = HCI_SCO_HDR_SIZE + h->dlen; - } else - return -EILSEQ; - break; - - case HCI_VENDOR_PKT: - if (count >= HCI_VENDOR_HDR_SIZE) { - struct hci_vendor_hdr *h = buf; - len = HCI_VENDOR_HDR_SIZE + - __le16_to_cpu(h->dlen); - } else - return -EILSEQ; - break; - } - - skb = bt_skb_alloc(len, GFP_ATOMIC); - if (!skb) { - BT_ERR("%s no memory for packet", hdev->name); - return -ENOMEM; - } - - data->rx_skb[queue] = skb; - - scb = (void *) skb->cb; - scb->type = type; - scb->expect = len; - } else { - /* Continuation */ - - scb = (void *) skb->cb; - len = scb->expect; - } - - len = min(len, count); - - memcpy(skb_put(skb, len), buf, len); - - scb->expect -= len; - - if (scb->expect == 0) { - /* Complete frame */ - - data->rx_skb[queue] = NULL; - - bt_cb(skb)->pkt_type = scb->type; - hci_recv_frame(hdev, skb); - } - - count -= len; buf += len; - } - - return 0; -} - static void bpa10x_tx_complete(struct urb *urb) { struct sk_buff *skb = urb->context; @@ -184,6 +80,22 @@ done: kfree_skb(skb); } +#define HCI_VENDOR_HDR_SIZE 5 + +#define HCI_RECV_VENDOR \ + .type = HCI_VENDOR_PKT, \ + .hlen = HCI_VENDOR_HDR_SIZE, \ + .loff = 3, \ + .lsize = 2, \ + .maxlen = HCI_MAX_FRAME_SIZE + +static const struct h4_recv_pkt bpa10x_recv_pkts[] = { + { H4_RECV_ACL, .recv = hci_recv_frame }, + { H4_RECV_SCO, .recv = hci_recv_frame }, + { H4_RECV_EVENT, .recv = hci_recv_frame }, + { HCI_RECV_VENDOR, .recv = hci_recv_diag }, +}; + static void bpa10x_rx_complete(struct urb *urb) { struct hci_dev *hdev = urb->context; @@ -197,11 +109,17 @@ static void bpa10x_rx_complete(struct urb *urb) return; if (urb->status == 0) { - if (bpa10x_recv(hdev, usb_pipebulk(urb->pipe), + bool idx = usb_pipebulk(urb->pipe); + + data->rx_skb[idx] = h4_recv_buf(hdev, data->rx_skb[idx], urb->transfer_buffer, - urb->actual_length) < 0) { + urb->actual_length, + bpa10x_recv_pkts, + ARRAY_SIZE(bpa10x_recv_pkts)); + if (IS_ERR(data->rx_skb[idx])) { BT_ERR("%s corrupted event packet", hdev->name); hdev->stat.err_rx++; + data->rx_skb[idx] = NULL; } } diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c index eec3f28..a6fce48 100644 --- a/drivers/bluetooth/hci_h4.c +++ b/drivers/bluetooth/hci_h4.c @@ -266,3 +266,4 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb, return skb; } +EXPORT_SYMBOL_GPL(h4_recv_buf); -- cgit v0.10.2 From fe806dceded462f7930f8ac4a41c5d19819e70b7 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 8 Oct 2015 03:14:28 +0200 Subject: Bluetooth: Enforce packet types in hci_recv_frame driver function When calling the hci_recv_frame driver function check for valid packet types that the core should process. This should catch issues with drivers trying to feed vendor packet types through this interface. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 43a1f2d..b2095ca 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3538,6 +3538,13 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb) return -ENXIO; } + if (bt_cb(skb)->pkt_type != HCI_EVENT_PKT && + bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT && + bt_cb(skb)->pkt_type != HCI_SCODATA_PKT) { + kfree_skb(skb); + return -EINVAL; + } + /* Incoming skb */ bt_cb(skb)->incoming = 1; -- cgit v0.10.2 From 301de2cb6a521405cde1a2f9cdc42c5257b5725b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 6 Oct 2015 13:03:19 +0300 Subject: Bluetooth: 6lowpan: Fix imtu & omtu values The omtu value is determined by the remote peer so there's no point in trying to hard-code it to any value. The IPSP specification otoh gives a more reasonable value for the imtu, i.e. 1280. Signed-off-by: Johan Hedberg Acked-by: Jukka Rissanen Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 131e79c..3e20f7a 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -775,8 +775,7 @@ static struct l2cap_chan *chan_create(void) chan->chan_type = L2CAP_CHAN_CONN_ORIENTED; chan->mode = L2CAP_MODE_LE_FLOWCTL; - chan->omtu = 65535; - chan->imtu = chan->omtu; + chan->imtu = 1280; return chan; } -- cgit v0.10.2 From 5d0fd77a043504dabccb66d9b5671e682868e96d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 6 Oct 2015 13:03:20 +0300 Subject: Bluetooth: 6lowpan: Remove redundant (and incorrect) MPS assignments The L2CAP core code already sets the local MPS to a sane value. The remote MPS value otoh comes from the remote side so there's no point in trying to hard-code it to any value. Signed-off-by: Johan Hedberg Acked-by: Jukka Rissanen Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 3e20f7a..3d951ab 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -788,9 +788,6 @@ static struct l2cap_chan *chan_open(struct l2cap_chan *pchan) if (!chan) return NULL; - chan->remote_mps = chan->omtu; - chan->mps = chan->omtu; - chan->state = BT_CONNECTED; return chan; -- cgit v0.10.2 From b0c09f94ff1660a1873549b788c998284ea5fb8a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 6 Oct 2015 13:03:21 +0300 Subject: Bluetooth: 6lowpan: Remove redundant BT_CONNECTED assignment The L2CAP core code makes sure of setting the channel state to BT_CONNECTED, so there's no need for the implementation code (6lowpan in this case) to do it. Signed-off-by: Johan Hedberg Acked-by: Jukka Rissanen Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 3d951ab..023fa29 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -788,8 +788,6 @@ static struct l2cap_chan *chan_open(struct l2cap_chan *pchan) if (!chan) return NULL; - chan->state = BT_CONNECTED; - return chan; } -- cgit v0.10.2 From 630ef791ea8e4274f20b833e1977cb1b0462d3ec Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 6 Oct 2015 13:03:22 +0300 Subject: Bluetooth: 6lowpan: Remove unnecessary chan_open() function All the chan_open() function now does is to call chan_create() so it doesn't really add any value. Signed-off-by: Johan Hedberg Acked-by: Jukka Rissanen Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 023fa29..77eb698 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -780,17 +780,6 @@ static struct l2cap_chan *chan_create(void) return chan; } -static struct l2cap_chan *chan_open(struct l2cap_chan *pchan) -{ - struct l2cap_chan *chan; - - chan = chan_create(); - if (!chan) - return NULL; - - return chan; -} - static void set_ip_addr_bits(u8 addr_type, u8 *addr) { if (addr_type == BDADDR_LE_PUBLIC) @@ -913,7 +902,10 @@ static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan) { struct l2cap_chan *chan; - chan = chan_open(pchan); + chan = chan_create(); + if (!chan) + return NULL; + chan->ops = pchan->ops; BT_DBG("chan %p pchan %p", chan, pchan); -- cgit v0.10.2 From 0cd088fc97bbe4834e9bc9727012ecac49386849 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 6 Oct 2015 13:03:23 +0300 Subject: Bluetooth: 6lowpan: Rename confusing 'pchan' variables The typical convention when having both a child and a parent channel variable is to call the former 'chan' and the latter 'pchan'. When there's only one variable it's called chan. Rename the 'pchan' variables in the 6lowpan code to follow this convention. Signed-off-by: Johan Hedberg Acked-by: Jukka Rissanen Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 77eb698..e20b972 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -1053,32 +1053,32 @@ static inline __u8 bdaddr_type(__u8 type) static struct l2cap_chan *chan_get(void) { - struct l2cap_chan *pchan; + struct l2cap_chan *chan; - pchan = chan_create(); - if (!pchan) + chan = chan_create(); + if (!chan) return NULL; - pchan->ops = &bt_6lowpan_chan_ops; + chan->ops = &bt_6lowpan_chan_ops; - return pchan; + return chan; } static int bt_6lowpan_connect(bdaddr_t *addr, u8 dst_type) { - struct l2cap_chan *pchan; + struct l2cap_chan *chan; int err; - pchan = chan_get(); - if (!pchan) + chan = chan_get(); + if (!chan) return -EINVAL; - err = l2cap_chan_connect(pchan, cpu_to_le16(L2CAP_PSM_IPSP), 0, + err = l2cap_chan_connect(chan, cpu_to_le16(L2CAP_PSM_IPSP), 0, addr, dst_type); - BT_DBG("chan %p err %d", pchan, err); + BT_DBG("chan %p err %d", chan, err); if (err < 0) - l2cap_chan_put(pchan); + l2cap_chan_put(chan); return err; } @@ -1103,31 +1103,31 @@ static int bt_6lowpan_disconnect(struct l2cap_conn *conn, u8 dst_type) static struct l2cap_chan *bt_6lowpan_listen(void) { bdaddr_t *addr = BDADDR_ANY; - struct l2cap_chan *pchan; + struct l2cap_chan *chan; int err; if (!enable_6lowpan) return NULL; - pchan = chan_get(); - if (!pchan) + chan = chan_get(); + if (!chan) return NULL; - pchan->state = BT_LISTEN; - pchan->src_type = BDADDR_LE_PUBLIC; + chan->state = BT_LISTEN; + chan->src_type = BDADDR_LE_PUBLIC; - atomic_set(&pchan->nesting, L2CAP_NESTING_PARENT); + atomic_set(&chan->nesting, L2CAP_NESTING_PARENT); - BT_DBG("chan %p src type %d", pchan, pchan->src_type); + BT_DBG("chan %p src type %d", chan, chan->src_type); - err = l2cap_add_psm(pchan, addr, cpu_to_le16(L2CAP_PSM_IPSP)); + err = l2cap_add_psm(chan, addr, cpu_to_le16(L2CAP_PSM_IPSP)); if (err) { - l2cap_chan_put(pchan); + l2cap_chan_put(chan); BT_ERR("psm cannot be added err %d", err); return NULL; } - return pchan; + return chan; } static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type, -- cgit v0.10.2 From 26d46dffbe2cd0a023aa6192708f80cd796af107 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 6 Oct 2015 13:03:24 +0300 Subject: Bluetooth: 6lowpan: Remove unnecessary chan_get() function The chan_get() function just adds unnecessary indirection to calling the chan_create() call. The only added value it gives is the chan->ops assignment, but that can equally well be done in the calling code. Signed-off-by: Johan Hedberg Acked-by: Jukka Rissanen Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index e20b972..9363f05 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -1051,28 +1051,17 @@ static inline __u8 bdaddr_type(__u8 type) return BDADDR_LE_RANDOM; } -static struct l2cap_chan *chan_get(void) -{ - struct l2cap_chan *chan; - - chan = chan_create(); - if (!chan) - return NULL; - - chan->ops = &bt_6lowpan_chan_ops; - - return chan; -} - static int bt_6lowpan_connect(bdaddr_t *addr, u8 dst_type) { struct l2cap_chan *chan; int err; - chan = chan_get(); + chan = chan_create(); if (!chan) return -EINVAL; + chan->ops = &bt_6lowpan_chan_ops; + err = l2cap_chan_connect(chan, cpu_to_le16(L2CAP_PSM_IPSP), 0, addr, dst_type); @@ -1109,10 +1098,11 @@ static struct l2cap_chan *bt_6lowpan_listen(void) if (!enable_6lowpan) return NULL; - chan = chan_get(); + chan = chan_create(); if (!chan) return NULL; + chan->ops = &bt_6lowpan_chan_ops; chan->state = BT_LISTEN; chan->src_type = BDADDR_LE_PUBLIC; -- cgit v0.10.2 From fd2874b3bbe832e90ac480971a7a8bd736b629b9 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 7 Oct 2015 16:48:32 -0500 Subject: ipv4: Fix ip_local_out_sk by passing the sk into __ip_local_out_sk In the rare case where sk != skb->sk ip_local_out_sk arranges to call dst->output differently if the skb is queued or not. This is a bug. Fix this bug by passing the sk parameter of ip_local_out_sk through from ip_local_out_sk to __ip_local_out_sk (skipping __ip_local_out). Fixes: 7026b1ddb6b8 ("netfilter: Pass socket pointer down through okfn().") Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 911ea73..6cb585a 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -117,7 +117,7 @@ int ip_local_out_sk(struct sock *sk, struct sk_buff *skb) { int err; - err = __ip_local_out(skb); + err = __ip_local_out_sk(sk, skb); if (likely(err == 1)) err = dst_output(sk, skb); -- cgit v0.10.2 From 850dcc4d4dd7d5da5c1b2a780c5e649c3b649545 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 7 Oct 2015 16:48:33 -0500 Subject: ipv4: Fix ip_queue_xmit to pass sk into ip_local_out_sk After a packet has been encapsulated by a tunnel we should use the tunnel sockets local multicast loopback flag to control if the encapsulated packet should be locally loopback back. Pass sk into ip_local_out_sk so that in the rare case we are dealing with a tunneled packet whose tunnel destination address is a multicast address the kernel properly decides to loopback this packet. In practice I don't think this matters as ip_queue_xmit is used by tcp, l2tp and sctp none of which I am aware of uses ip level multicasting as they are all point to point communications protocols. Let's fix this before someone uses ip_queue_xmit for a tunnel protocol that does use multicast. Fixes: aad88724c9d5 ("ipv4: add a sock pointer to dst->output() path.") Fixes: b0270e91014d ("ipv4: add a sock pointer to ip_queue_xmit()") Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 6cb585a..1030f48 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -460,7 +460,7 @@ packet_routed: skb->priority = sk->sk_priority; skb->mark = sk->sk_mark; - res = ip_local_out(skb); + res = ip_local_out_sk(sk, skb); rcu_read_unlock(); return res; -- cgit v0.10.2 From 3f5312ae620c79e877a6aa0e8894c6603a090b4e Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 7 Oct 2015 16:48:34 -0500 Subject: xfrm: Only compute net once in xfrm_policy_queue_process Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 418daa0..be1776b 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1887,6 +1887,7 @@ static void xfrm_policy_queue_process(unsigned long arg) struct sock *sk; struct dst_entry *dst; struct xfrm_policy *pol = (struct xfrm_policy *)arg; + struct net *net = xp_net(pol); struct xfrm_policy_queue *pq = &pol->polq; struct flowi fl; struct sk_buff_head list; @@ -1903,8 +1904,7 @@ static void xfrm_policy_queue_process(unsigned long arg) spin_unlock(&pq->hold_queue.lock); dst_hold(dst->path); - dst = xfrm_lookup(xp_net(pol), dst->path, &fl, - sk, 0); + dst = xfrm_lookup(net, dst->path, &fl, sk, 0); if (IS_ERR(dst)) goto purge_queue; @@ -1934,8 +1934,7 @@ static void xfrm_policy_queue_process(unsigned long arg) xfrm_decode_session(skb, &fl, skb_dst(skb)->ops->family); dst_hold(skb_dst(skb)->path); - dst = xfrm_lookup(xp_net(pol), skb_dst(skb)->path, - &fl, skb->sk, 0); + dst = xfrm_lookup(net, skb_dst(skb)->path, &fl, skb->sk, 0); if (IS_ERR(dst)) { kfree_skb(skb); continue; -- cgit v0.10.2 From 13206b6bff3b15b724926a222406476bf2c23c40 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 7 Oct 2015 16:48:35 -0500 Subject: net: Pass net into dst_output and remove dst_output_okfn Replace dst_output_okfn with dst_output Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller diff --git a/include/net/dst.h b/include/net/dst.h index 779206c..fdd01fe 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -454,14 +454,10 @@ static inline void dst_set_expires(struct dst_entry *dst, int timeout) } /* Output packet to network from transport. */ -static inline int dst_output(struct sock *sk, struct sk_buff *skb) +static inline int dst_output(struct net *net, struct sock *sk, struct sk_buff *skb) { return skb_dst(skb)->output(sk, skb); } -static inline int dst_output_okfn(struct net *net, struct sock *sk, struct sk_buff *skb) -{ - return dst_output(sk, skb); -} /* Input packet from network to transport. */ static inline int dst_input(struct sk_buff *skb) diff --git a/net/decnet/dn_nsp_out.c b/net/decnet/dn_nsp_out.c index 4b02dd3..849805e 100644 --- a/net/decnet/dn_nsp_out.c +++ b/net/decnet/dn_nsp_out.c @@ -85,7 +85,7 @@ static void dn_nsp_send(struct sk_buff *skb) if (dst) { try_again: skb_dst_set(skb, dst); - dst_output(skb->sk, skb); + dst_output(&init_net, skb->sk, skb); return; } @@ -582,7 +582,7 @@ static __inline__ void dn_nsp_do_disc(struct sock *sk, unsigned char msgflg, * associations. */ skb_dst_set(skb, dst_clone(dst)); - dst_output(skb->sk, skb); + dst_output(&init_net, skb->sk, skb); } diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index d66cfb3..da0d7ce 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -72,7 +72,7 @@ static int ip_forward_finish(struct net *net, struct sock *sk, struct sk_buff *s ip_forward_options(skb); skb_sender_cpu_clear(skb); - return dst_output(sk, skb); + return dst_output(net, sk, skb); } int ip_forward(struct sk_buff *skb) diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 1030f48..c94efb2 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -105,7 +105,7 @@ static int __ip_local_out_sk(struct sock *sk, struct sk_buff *skb) ip_send_check(iph); return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, net, sk, skb, NULL, skb_dst(skb)->dev, - dst_output_okfn); + dst_output); } int __ip_local_out(struct sk_buff *skb) @@ -115,11 +115,12 @@ int __ip_local_out(struct sk_buff *skb) int ip_local_out_sk(struct sock *sk, struct sk_buff *skb) { + struct net *net = dev_net(skb_dst(skb)->dev); int err; err = __ip_local_out_sk(sk, skb); if (likely(err == 1)) - err = dst_output(sk, skb); + err = dst_output(net, sk, skb); return err; } @@ -276,7 +277,7 @@ static int ip_finish_output(struct net *net, struct sock *sk, struct sk_buff *sk /* Policy lookup after SNAT yielded a new policy */ if (skb_dst(skb)->xfrm) { IPCB(skb)->flags |= IPSKB_REROUTED; - return dst_output(sk, skb); + return dst_output(net, sk, skb); } #endif mtu = ip_skb_dst_mtu(skb); diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index 3b87ec5..4d8f0b6 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -197,7 +197,7 @@ static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev, skb_dst_set(skb, dst); skb->dev = skb_dst(skb)->dev; - err = dst_output(skb->sk, skb); + err = dst_output(tunnel->net, skb->sk, skb); if (net_xmit_eval(err) == 0) err = skb->len; iptunnel_xmit_stats(err, &dev->stats, dev->tstats); diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index cfcb996..fc42525 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -1689,7 +1689,7 @@ static inline int ipmr_forward_finish(struct net *net, struct sock *sk, if (unlikely(opt->optlen)) ip_forward_options(skb); - return dst_output(sk, skb); + return dst_output(net, sk, skb); } /* diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 09a07e8..8c0d0bd 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -413,7 +413,7 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4, err = NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_OUT, net, sk, skb, NULL, rt->dst.dev, - dst_output_okfn); + dst_output); if (err > 0) err = net_xmit_errno(err); if (err) diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c index cd6be73..17db61f 100644 --- a/net/ipv4/xfrm4_output.c +++ b/net/ipv4/xfrm4_output.c @@ -87,7 +87,7 @@ static int __xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb) #ifdef CONFIG_NETFILTER if (!x) { IPCB(skb)->flags |= IPSKB_REROUTED; - return dst_output(sk, skb); + return dst_output(net, sk, skb); } #endif diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index caf7d14..0171e76 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -233,7 +233,7 @@ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6, */ return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net, (struct sock *)sk, skb, NULL, dst->dev, - dst_output_okfn); + dst_output); } skb->dev = dst->dev; @@ -333,7 +333,7 @@ static inline int ip6_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb) { skb_sender_cpu_clear(skb); - return dst_output(sk, skb); + return dst_output(net, sk, skb); } static unsigned int ip6_dst_mtu_forward(const struct dst_entry *dst) diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index f96f1c1..0a8610b 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -482,7 +482,7 @@ vti6_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) return -EMSGSIZE; } - err = dst_output(skb->sk, skb); + err = dst_output(t->net, skb->sk, skb); if (net_xmit_eval(err) == 0) { struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats); diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 5e5d16e..ad19136 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -1991,7 +1991,7 @@ static inline int ip6mr_forward2_finish(struct net *net, struct sock *sk, struct IPSTATS_MIB_OUTFORWDATAGRAMS); IP6_ADD_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_OUTOCTETS, skb->len); - return dst_output(sk, skb); + return dst_output(net, sk, skb); } /* diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index a8bf57c..124338a 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -1646,7 +1646,7 @@ static void mld_sendpack(struct sk_buff *skb) err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net, net->ipv6.igmp_sk, skb, NULL, skb->dev, - dst_output_okfn); + dst_output); out: if (!err) { ICMP6MSGOUT_INC_STATS(net, idev, ICMPV6_MLD2_REPORT); @@ -2010,7 +2010,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) skb_dst_set(skb, dst); err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net, sk, skb, NULL, skb->dev, - dst_output_okfn); + dst_output); out: if (!err) { ICMP6MSGOUT_INC_STATS(net, idev, type); diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 7089c30..b18012f 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -465,7 +465,7 @@ static void ndisc_send_skb(struct sk_buff *skb, err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net, sk, skb, NULL, dst->dev, - dst_output_okfn); + dst_output); if (!err) { ICMP6MSGOUT_INC_STATS(net, idev, type); ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c index e77102c..4337147 100644 --- a/net/ipv6/output_core.c +++ b/net/ipv6/output_core.c @@ -151,7 +151,7 @@ static int __ip6_local_out_sk(struct sock *sk, struct sk_buff *skb) return nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net, sk, skb, NULL, skb_dst(skb)->dev, - dst_output_okfn); + dst_output); } int __ip6_local_out(struct sk_buff *skb) @@ -162,11 +162,12 @@ EXPORT_SYMBOL_GPL(__ip6_local_out); int ip6_local_out_sk(struct sock *sk, struct sk_buff *skb) { + struct net *net = dev_net(skb_dst(skb)->dev); int err; err = __ip6_local_out_sk(sk, skb); if (likely(err == 1)) - err = dst_output(sk, skb); + err = dst_output(net, sk, skb); return err; } diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index fec0151..dc65ec1 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -655,7 +655,7 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length, IP6_UPD_PO_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUT, skb->len); err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net, sk, skb, - NULL, rt->dst.dev, dst_output_okfn); + NULL, rt->dst.dev, dst_output); if (err > 0) err = net_xmit_errno(err); if (err) diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c index 4cefda0..c9a5bd5 100644 --- a/net/ipv6/xfrm6_output.c +++ b/net/ipv6/xfrm6_output.c @@ -147,7 +147,7 @@ static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb) #ifdef CONFIG_NETFILTER if (!x) { IP6CB(skb)->flags |= IP6SKB_REROUTED; - return dst_output(sk, skb); + return dst_output(net, sk, skb); } #endif diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 77182b9..504d1fc 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -576,7 +576,7 @@ static inline int ip_vs_nat_send_or_cont(int pf, struct sk_buff *skb, if (!skb->sk) skb_sender_cpu_clear(skb); NF_HOOK(pf, NF_INET_LOCAL_OUT, cp->ipvs->net, NULL, skb, - NULL, skb_dst(skb)->dev, dst_output_okfn); + NULL, skb_dst(skb)->dev, dst_output); } else ret = NF_ACCEPT; @@ -598,7 +598,7 @@ static inline int ip_vs_send_or_cont(int pf, struct sk_buff *skb, if (!skb->sk) skb_sender_cpu_clear(skb); NF_HOOK(pf, NF_INET_LOCAL_OUT, cp->ipvs->net, NULL, skb, - NULL, skb_dst(skb)->dev, dst_output_okfn); + NULL, skb_dst(skb)->dev, dst_output); } else ret = NF_ACCEPT; return ret; diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c index c48a4b8..88752b0 100644 --- a/net/xfrm/xfrm_output.c +++ b/net/xfrm/xfrm_output.c @@ -141,7 +141,7 @@ int xfrm_output_resume(struct sk_buff *skb, int err) goto out; if (!skb_dst(skb)->xfrm) - return dst_output(skb->sk, skb); + return dst_output(net, skb->sk, skb); err = nf_hook(skb_dst(skb)->ops->family, NF_INET_POST_ROUTING, net, skb->sk, skb, diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index be1776b..f4f2d98 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1944,7 +1944,7 @@ static void xfrm_policy_queue_process(unsigned long arg) skb_dst_drop(skb); skb_dst_set(skb, dst); - dst_output(skb->sk, skb); + dst_output(net, skb->sk, skb); } out: -- cgit v0.10.2 From 4ebdfba73c09d8568d891bae87c40fad43dd7f41 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 7 Oct 2015 16:48:36 -0500 Subject: dst: Pass a sk into .local_out For consistency with the other similar methods in the kernel pass a struct sock into the dst_ops .local_out method. Simplifying the socket passing case is needed a prequel to passing a struct net reference into .local_out. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 6449976..1039eb5 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -74,9 +74,9 @@ static struct dst_entry *vrf_ip_check(struct dst_entry *dst, u32 cookie) return dst; } -static int vrf_ip_local_out(struct sk_buff *skb) +static int vrf_ip_local_out(struct sock *sk, struct sk_buff *skb) { - return ip_local_out(skb); + return ip_local_out_sk(sk, skb); } static unsigned int vrf_v4_mtu(const struct dst_entry *dst) diff --git a/include/net/dst_ops.h b/include/net/dst_ops.h index d642539..3f26a6a 100644 --- a/include/net/dst_ops.h +++ b/include/net/dst_ops.h @@ -28,7 +28,7 @@ struct dst_ops { struct sk_buff *skb, u32 mtu); void (*redirect)(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb); - int (*local_out)(struct sk_buff *skb); + int (*local_out)(struct sock *sk, struct sk_buff *skb); struct neighbour * (*neigh_lookup)(const struct dst_entry *dst, struct sk_buff *skb, const void *daddr); diff --git a/include/net/ip.h b/include/net/ip.h index dd06ab3..ea1f721 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -112,6 +112,7 @@ int ip_mc_output(struct sock *sk, struct sk_buff *skb); int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, int (*output)(struct net *, struct sock *, struct sk_buff *)); void ip_send_check(struct iphdr *ip); +int __ip_local_out_sk(struct sock *sk, struct sk_buff *skb); int __ip_local_out(struct sk_buff *skb); int ip_local_out_sk(struct sock *sk, struct sk_buff *skb); static inline int ip_local_out(struct sk_buff *skb) diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 3dde042..5692026 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -865,6 +865,7 @@ int ip6_forward(struct sk_buff *skb); int ip6_input(struct sk_buff *skb); int ip6_mc_input(struct sk_buff *skb); +int __ip6_local_out_sk(struct sock *sk, struct sk_buff *skb); int __ip6_local_out(struct sk_buff *skb); int ip6_local_out_sk(struct sock *sk, struct sk_buff *skb); int ip6_local_out(struct sk_buff *skb); diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index c94efb2..c38dfd7 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -96,7 +96,7 @@ void ip_send_check(struct iphdr *iph) } EXPORT_SYMBOL(ip_send_check); -static int __ip_local_out_sk(struct sock *sk, struct sk_buff *skb) +int __ip_local_out_sk(struct sock *sk, struct sk_buff *skb) { struct net *net = dev_net(skb_dst(skb)->dev); struct iphdr *iph = ip_hdr(skb); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index bf1486b..638b976 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -165,7 +165,7 @@ static struct dst_ops ipv4_dst_ops = { .link_failure = ipv4_link_failure, .update_pmtu = ip_rt_update_pmtu, .redirect = ip_do_redirect, - .local_out = __ip_local_out, + .local_out = __ip_local_out_sk, .neigh_lookup = ipv4_neigh_lookup, }; diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index f2606b9..d46d99f 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -243,7 +243,7 @@ static struct dst_ops xfrm4_dst_ops = { .cow_metrics = dst_cow_metrics_generic, .destroy = xfrm4_dst_destroy, .ifdown = xfrm4_dst_ifdown, - .local_out = __ip_local_out, + .local_out = __ip_local_out_sk, .gc_thresh = 32768, }; diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c index 4337147..e5affb5 100644 --- a/net/ipv6/output_core.c +++ b/net/ipv6/output_core.c @@ -138,7 +138,7 @@ int ip6_dst_hoplimit(struct dst_entry *dst) EXPORT_SYMBOL(ip6_dst_hoplimit); #endif -static int __ip6_local_out_sk(struct sock *sk, struct sk_buff *skb) +int __ip6_local_out_sk(struct sock *sk, struct sk_buff *skb) { struct net *net = dev_net(skb_dst(skb)->dev); int len; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index d3d9467..b62a507 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -226,7 +226,7 @@ static struct dst_ops ip6_dst_ops_template = { .link_failure = ip6_link_failure, .update_pmtu = ip6_rt_update_pmtu, .redirect = rt6_do_redirect, - .local_out = __ip6_local_out, + .local_out = __ip6_local_out_sk, .neigh_lookup = ip6_neigh_lookup, }; diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 08c9c93..f787683 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -285,7 +285,7 @@ static struct dst_ops xfrm6_dst_ops = { .cow_metrics = dst_cow_metrics_generic, .destroy = xfrm6_dst_destroy, .ifdown = xfrm6_dst_ifdown, - .local_out = __ip6_local_out, + .local_out = __ip6_local_out_sk, .gc_thresh = 32768, }; diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c index 88752b0..a7a254f 100644 --- a/net/xfrm/xfrm_output.c +++ b/net/xfrm/xfrm_output.c @@ -136,7 +136,7 @@ int xfrm_output_resume(struct sk_buff *skb, int err) while (likely((err = xfrm_output_one(skb, err)) == 0)) { nf_reset(skb); - err = skb_dst(skb)->ops->local_out(skb); + err = skb_dst(skb)->ops->local_out(skb->sk, skb); if (unlikely(err != 1)) goto out; -- cgit v0.10.2 From b92dacd45698e120104ff81066ceb534916090d9 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 7 Oct 2015 16:48:37 -0500 Subject: ipv4: Merge __ip_local_out and __ip_local_out_sk Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller diff --git a/include/net/ip.h b/include/net/ip.h index ea1f721..46272e0 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -112,8 +112,7 @@ int ip_mc_output(struct sock *sk, struct sk_buff *skb); int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, int (*output)(struct net *, struct sock *, struct sk_buff *)); void ip_send_check(struct iphdr *ip); -int __ip_local_out_sk(struct sock *sk, struct sk_buff *skb); -int __ip_local_out(struct sk_buff *skb); +int __ip_local_out(struct sock *sk, struct sk_buff *skb); int ip_local_out_sk(struct sock *sk, struct sk_buff *skb); static inline int ip_local_out(struct sk_buff *skb) { diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index c38dfd7..66c627b 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -96,7 +96,7 @@ void ip_send_check(struct iphdr *iph) } EXPORT_SYMBOL(ip_send_check); -int __ip_local_out_sk(struct sock *sk, struct sk_buff *skb) +int __ip_local_out(struct sock *sk, struct sk_buff *skb) { struct net *net = dev_net(skb_dst(skb)->dev); struct iphdr *iph = ip_hdr(skb); @@ -108,17 +108,12 @@ int __ip_local_out_sk(struct sock *sk, struct sk_buff *skb) dst_output); } -int __ip_local_out(struct sk_buff *skb) -{ - return __ip_local_out_sk(skb->sk, skb); -} - int ip_local_out_sk(struct sock *sk, struct sk_buff *skb) { struct net *net = dev_net(skb_dst(skb)->dev); int err; - err = __ip_local_out_sk(sk, skb); + err = __ip_local_out(sk, skb); if (likely(err == 1)) err = dst_output(net, sk, skb); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 638b976..bf1486b 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -165,7 +165,7 @@ static struct dst_ops ipv4_dst_ops = { .link_failure = ipv4_link_failure, .update_pmtu = ip_rt_update_pmtu, .redirect = ip_do_redirect, - .local_out = __ip_local_out_sk, + .local_out = __ip_local_out, .neigh_lookup = ipv4_neigh_lookup, }; diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index d46d99f..f2606b9 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -243,7 +243,7 @@ static struct dst_ops xfrm4_dst_ops = { .cow_metrics = dst_cow_metrics_generic, .destroy = xfrm4_dst_destroy, .ifdown = xfrm4_dst_ifdown, - .local_out = __ip_local_out_sk, + .local_out = __ip_local_out, .gc_thresh = 32768, }; -- cgit v0.10.2 From e2cb77db089796f163092326ca25512845df7a3a Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 7 Oct 2015 16:48:38 -0500 Subject: ipv4: Merge ip_local_out and ip_local_out_sk It is confusing and silly hiding a parameter so modify all of the callers to pass in the appropriate socket or skb->sk if no socket is known. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c index 207f62e..c75ad39 100644 --- a/drivers/net/ipvlan/ipvlan_core.c +++ b/drivers/net/ipvlan/ipvlan_core.c @@ -364,7 +364,7 @@ static int ipvlan_process_v4_outbound(struct sk_buff *skb) } skb_dst_drop(skb); skb_dst_set(skb, &rt->dst); - err = ip_local_out(skb); + err = ip_local_out(skb->sk, skb); if (unlikely(net_xmit_eval(err))) dev->stats.tx_errors++; else diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c index 686f37d..6bef7be 100644 --- a/drivers/net/ppp/pptp.c +++ b/drivers/net/ppp/pptp.c @@ -282,7 +282,7 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb) ip_select_ident(sock_net(sk), skb, NULL); ip_send_check(iph); - ip_local_out(skb); + ip_local_out(skb->sk, skb); return 1; tx_error: diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 1039eb5..231f9d8 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -76,7 +76,7 @@ static struct dst_entry *vrf_ip_check(struct dst_entry *dst, u32 cookie) static int vrf_ip_local_out(struct sock *sk, struct sk_buff *skb) { - return ip_local_out_sk(sk, skb); + return ip_local_out(sk, skb); } static unsigned int vrf_v4_mtu(const struct dst_entry *dst) @@ -222,7 +222,7 @@ static netdev_tx_t vrf_process_v4_outbound(struct sk_buff *skb, RT_SCOPE_LINK); } - ret = ip_local_out(skb); + ret = ip_local_out(skb->sk, skb); if (unlikely(net_xmit_eval(ret))) vrf_dev->stats.tx_errors++; else diff --git a/include/net/ip.h b/include/net/ip.h index 46272e0..03e80f9 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -113,11 +113,7 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, int (*output)(struct net *, struct sock *, struct sk_buff *)); void ip_send_check(struct iphdr *ip); int __ip_local_out(struct sock *sk, struct sk_buff *skb); -int ip_local_out_sk(struct sock *sk, struct sk_buff *skb); -static inline int ip_local_out(struct sk_buff *skb) -{ - return ip_local_out_sk(skb->sk, skb); -} +int ip_local_out(struct sock *sk, struct sk_buff *skb); int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl); void ip_init(void); diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index de6d4c8..43375d9 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -397,7 +397,7 @@ static int igmpv3_sendpack(struct sk_buff *skb) pig->csum = ip_compute_csum(igmp_hdr(skb), igmplen); - return ip_local_out(skb); + return ip_local_out(skb->sk, skb); } static int grec_size(struct ip_mc_list *pmc, int type, int gdel, int sdel) @@ -739,7 +739,7 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc, ih->group = group; ih->csum = ip_compute_csum((void *)ih, sizeof(struct igmphdr)); - return ip_local_out(skb); + return ip_local_out(skb->sk, skb); } static void igmp_gq_timer_expire(unsigned long data) diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 66c627b..10366ee 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -108,7 +108,7 @@ int __ip_local_out(struct sock *sk, struct sk_buff *skb) dst_output); } -int ip_local_out_sk(struct sock *sk, struct sk_buff *skb) +int ip_local_out(struct sock *sk, struct sk_buff *skb) { struct net *net = dev_net(skb_dst(skb)->dev); int err; @@ -119,7 +119,7 @@ int ip_local_out_sk(struct sock *sk, struct sk_buff *skb) return err; } -EXPORT_SYMBOL_GPL(ip_local_out_sk); +EXPORT_SYMBOL_GPL(ip_local_out); static inline int ip_select_ttl(struct inet_sock *inet, struct dst_entry *dst) { @@ -169,7 +169,7 @@ int ip_build_and_send_pkt(struct sk_buff *skb, const struct sock *sk, skb->mark = sk->sk_mark; /* Send it out. */ - return ip_local_out(skb); + return ip_local_out(skb->sk, skb); } EXPORT_SYMBOL_GPL(ip_build_and_send_pkt); @@ -456,7 +456,7 @@ packet_routed: skb->priority = sk->sk_priority; skb->mark = sk->sk_mark; - res = ip_local_out_sk(sk, skb); + res = ip_local_out(sk, skb); rcu_read_unlock(); return res; @@ -1436,7 +1436,7 @@ int ip_send_skb(struct net *net, struct sk_buff *skb) { int err; - err = ip_local_out(skb); + err = ip_local_out(skb->sk, skb); if (err) { if (err > 0) err = net_xmit_errno(err); diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c index 84dce6a..8d85ecd 100644 --- a/net/ipv4/ip_tunnel_core.c +++ b/net/ipv4/ip_tunnel_core.c @@ -79,7 +79,7 @@ int iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb, __ip_select_ident(dev_net(rt->dst.dev), iph, skb_shinfo(skb)->gso_segs ?: 1); - err = ip_local_out_sk(sk, skb); + err = ip_local_out(sk, skb); if (unlikely(net_xmit_eval(err))) pkt_len = 0; return pkt_len; diff --git a/net/ipv4/netfilter/ipt_SYNPROXY.c b/net/ipv4/netfilter/ipt_SYNPROXY.c index 6a6e762..473faf7 100644 --- a/net/ipv4/netfilter/ipt_SYNPROXY.c +++ b/net/ipv4/netfilter/ipt_SYNPROXY.c @@ -63,7 +63,7 @@ synproxy_send_tcp(const struct synproxy_net *snet, nf_conntrack_get(nfct); } - ip_local_out(nskb); + ip_local_out(nskb->sk, nskb); return; free_nskb: diff --git a/net/ipv4/netfilter/nf_dup_ipv4.c b/net/ipv4/netfilter/nf_dup_ipv4.c index ce2a59e..0b9abfb 100644 --- a/net/ipv4/netfilter/nf_dup_ipv4.c +++ b/net/ipv4/netfilter/nf_dup_ipv4.c @@ -92,7 +92,7 @@ void nf_dup_ipv4(struct net *net, struct sk_buff *skb, unsigned int hooknum, if (nf_dup_ipv4_route(net, skb, gw, oif)) { __this_cpu_write(nf_skb_duplicated, true); - ip_local_out(skb); + ip_local_out(skb->sk, skb); __this_cpu_write(nf_skb_duplicated, false); } else { kfree_skb(skb); diff --git a/net/ipv4/netfilter/nf_reject_ipv4.c b/net/ipv4/netfilter/nf_reject_ipv4.c index 2f5e925..dcc125c 100644 --- a/net/ipv4/netfilter/nf_reject_ipv4.c +++ b/net/ipv4/netfilter/nf_reject_ipv4.c @@ -157,7 +157,7 @@ void nf_send_reset(struct net *net, struct sk_buff *oldskb, int hook) dev_queue_xmit(nskb); } else #endif - ip_local_out(nskb); + ip_local_out(nskb->sk, nskb); return; diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 504d1fc..d77503e 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -1049,7 +1049,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, ret = ip_vs_tunnel_xmit_prepare(skb, cp); if (ret == NF_ACCEPT) - ip_local_out(skb); + ip_local_out(skb->sk, skb); else if (ret == NF_DROP) kfree_skb(skb); rcu_read_unlock(); -- cgit v0.10.2 From 9f8955cc468ddb7d08a0e614a45f9a82c4019b00 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 7 Oct 2015 16:48:39 -0500 Subject: ipv6: Merge __ip6_local_out and __ip6_local_out_sk Only __ip6_local_out_sk has callers so rename __ip6_local_out_sk __ip6_local_out and remove the previous __ip6_local_out. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 5692026..be7e768 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -865,8 +865,7 @@ int ip6_forward(struct sk_buff *skb); int ip6_input(struct sk_buff *skb); int ip6_mc_input(struct sk_buff *skb); -int __ip6_local_out_sk(struct sock *sk, struct sk_buff *skb); -int __ip6_local_out(struct sk_buff *skb); +int __ip6_local_out(struct sock *sk, struct sk_buff *skb); int ip6_local_out_sk(struct sock *sk, struct sk_buff *skb); int ip6_local_out(struct sk_buff *skb); diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c index e5affb5..f93ae15 100644 --- a/net/ipv6/output_core.c +++ b/net/ipv6/output_core.c @@ -138,7 +138,7 @@ int ip6_dst_hoplimit(struct dst_entry *dst) EXPORT_SYMBOL(ip6_dst_hoplimit); #endif -int __ip6_local_out_sk(struct sock *sk, struct sk_buff *skb) +int __ip6_local_out(struct sock *sk, struct sk_buff *skb) { struct net *net = dev_net(skb_dst(skb)->dev); int len; @@ -153,11 +153,6 @@ int __ip6_local_out_sk(struct sock *sk, struct sk_buff *skb) net, sk, skb, NULL, skb_dst(skb)->dev, dst_output); } - -int __ip6_local_out(struct sk_buff *skb) -{ - return __ip6_local_out_sk(skb->sk, skb); -} EXPORT_SYMBOL_GPL(__ip6_local_out); int ip6_local_out_sk(struct sock *sk, struct sk_buff *skb) @@ -165,7 +160,7 @@ int ip6_local_out_sk(struct sock *sk, struct sk_buff *skb) struct net *net = dev_net(skb_dst(skb)->dev); int err; - err = __ip6_local_out_sk(sk, skb); + err = __ip6_local_out(sk, skb); if (likely(err == 1)) err = dst_output(net, sk, skb); diff --git a/net/ipv6/route.c b/net/ipv6/route.c index b62a507..d3d9467 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -226,7 +226,7 @@ static struct dst_ops ip6_dst_ops_template = { .link_failure = ip6_link_failure, .update_pmtu = ip6_rt_update_pmtu, .redirect = rt6_do_redirect, - .local_out = __ip6_local_out_sk, + .local_out = __ip6_local_out, .neigh_lookup = ip6_neigh_lookup, }; diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index f787683..08c9c93 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -285,7 +285,7 @@ static struct dst_ops xfrm6_dst_ops = { .cow_metrics = dst_cow_metrics_generic, .destroy = xfrm6_dst_destroy, .ifdown = xfrm6_dst_ifdown, - .local_out = __ip6_local_out_sk, + .local_out = __ip6_local_out, .gc_thresh = 32768, }; -- cgit v0.10.2 From 792883303cdb3a7edd16017d7aba53926189ef41 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 7 Oct 2015 16:48:40 -0500 Subject: ipv6: Merge ip6_local_out and ip6_local_out_sk Stop hidding the sk parameter with an inline helper function and make all of the callers pass it, so that it is clear what the function is doing. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c index c75ad39..75dcf36 100644 --- a/drivers/net/ipvlan/ipvlan_core.c +++ b/drivers/net/ipvlan/ipvlan_core.c @@ -401,7 +401,7 @@ static int ipvlan_process_v6_outbound(struct sk_buff *skb) } skb_dst_drop(skb); skb_dst_set(skb, dst); - err = ip6_local_out(skb); + err = ip6_local_out(skb->sk, skb); if (unlikely(net_xmit_eval(err))) dev->stats.tx_errors++; else diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h index fa915fa..8f18a8b 100644 --- a/include/net/ip6_tunnel.h +++ b/include/net/ip6_tunnel.h @@ -87,7 +87,7 @@ static inline void ip6tunnel_xmit(struct sock *sk, struct sk_buff *skb, int pkt_len, err; pkt_len = skb->len - skb_inner_network_offset(skb); - err = ip6_local_out_sk(sk, skb); + err = ip6_local_out(sk, skb); if (net_xmit_eval(err) == 0) { struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats); diff --git a/include/net/ipv6.h b/include/net/ipv6.h index be7e768..30eb182 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -866,8 +866,7 @@ int ip6_input(struct sk_buff *skb); int ip6_mc_input(struct sk_buff *skb); int __ip6_local_out(struct sock *sk, struct sk_buff *skb); -int ip6_local_out_sk(struct sock *sk, struct sk_buff *skb); -int ip6_local_out(struct sk_buff *skb); +int ip6_local_out(struct sock *sk, struct sk_buff *skb); /* * Extension header (options) processing diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 0171e76..31c686b 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1692,7 +1692,7 @@ int ip6_send_skb(struct sk_buff *skb) struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); int err; - err = ip6_local_out(skb); + err = ip6_local_out(skb->sk, skb); if (err) { if (err > 0) err = net_xmit_errno(err); diff --git a/net/ipv6/netfilter/ip6t_SYNPROXY.c b/net/ipv6/netfilter/ip6t_SYNPROXY.c index c235660..c38c341 100644 --- a/net/ipv6/netfilter/ip6t_SYNPROXY.c +++ b/net/ipv6/netfilter/ip6t_SYNPROXY.c @@ -76,7 +76,7 @@ synproxy_send_tcp(const struct synproxy_net *snet, nf_conntrack_get(nfct); } - ip6_local_out(nskb); + ip6_local_out(nskb->sk, nskb); return; free_nskb: diff --git a/net/ipv6/netfilter/nf_dup_ipv6.c b/net/ipv6/netfilter/nf_dup_ipv6.c index ee0d9a5..64f3fe5 100644 --- a/net/ipv6/netfilter/nf_dup_ipv6.c +++ b/net/ipv6/netfilter/nf_dup_ipv6.c @@ -68,7 +68,7 @@ void nf_dup_ipv6(struct net *net, struct sk_buff *skb, unsigned int hooknum, } if (nf_dup_ipv6_route(net, skb, gw, oif)) { __this_cpu_write(nf_skb_duplicated, true); - ip6_local_out(skb); + ip6_local_out(skb->sk, skb); __this_cpu_write(nf_skb_duplicated, false); } else { kfree_skb(skb); diff --git a/net/ipv6/netfilter/nf_reject_ipv6.c b/net/ipv6/netfilter/nf_reject_ipv6.c index 94b4c6d..a4f73e2 100644 --- a/net/ipv6/netfilter/nf_reject_ipv6.c +++ b/net/ipv6/netfilter/nf_reject_ipv6.c @@ -206,7 +206,7 @@ void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook) dev_queue_xmit(nskb); } else #endif - ip6_local_out(nskb); + ip6_local_out(nskb->sk, nskb); } EXPORT_SYMBOL_GPL(nf_send_reset6); diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c index f93ae15..1285581 100644 --- a/net/ipv6/output_core.c +++ b/net/ipv6/output_core.c @@ -155,7 +155,7 @@ int __ip6_local_out(struct sock *sk, struct sk_buff *skb) } EXPORT_SYMBOL_GPL(__ip6_local_out); -int ip6_local_out_sk(struct sock *sk, struct sk_buff *skb) +int ip6_local_out(struct sock *sk, struct sk_buff *skb) { struct net *net = dev_net(skb_dst(skb)->dev); int err; @@ -166,10 +166,4 @@ int ip6_local_out_sk(struct sock *sk, struct sk_buff *skb) return err; } -EXPORT_SYMBOL_GPL(ip6_local_out_sk); - -int ip6_local_out(struct sk_buff *skb) -{ - return ip6_local_out_sk(skb->sk, skb); -} EXPORT_SYMBOL_GPL(ip6_local_out); diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index d77503e..2042b93 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -1141,7 +1141,7 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ret = ip_vs_tunnel_xmit_prepare(skb, cp); if (ret == NF_ACCEPT) - ip6_local_out(skb); + ip6_local_out(skb->sk, skb); else if (ret == NF_DROP) kfree_skb(skb); rcu_read_unlock(); -- cgit v0.10.2 From f859b0f662493e4f53d462f5759e3c4302933077 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 7 Oct 2015 16:48:41 -0500 Subject: ipv4: Cache net in iptunnel_xmit Store net in a variable in ip_tunnel_xmit so it does not need to be recomputed when it is used again. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c index 8d85ecd..caef8e2 100644 --- a/net/ipv4/ip_tunnel_core.c +++ b/net/ipv4/ip_tunnel_core.c @@ -53,6 +53,7 @@ int iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb, __u8 tos, __u8 ttl, __be16 df, bool xnet) { int pkt_len = skb->len - skb_inner_network_offset(skb); + struct net *net = dev_net(rt->dst.dev); struct iphdr *iph; int err; @@ -76,8 +77,7 @@ int iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb, iph->daddr = dst; iph->saddr = src; iph->ttl = ttl; - __ip_select_ident(dev_net(rt->dst.dev), iph, - skb_shinfo(skb)->gso_segs ?: 1); + __ip_select_ident(net, iph, skb_shinfo(skb)->gso_segs ?: 1); err = ip_local_out(sk, skb); if (unlikely(net_xmit_eval(err))) -- cgit v0.10.2 From 77589ce0f84dd99cc946fd71fe6fb44dd8220d0a Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 7 Oct 2015 16:48:42 -0500 Subject: ipv4: Cache net in ip_build_and_send_pkt and ip_queue_xmit Compute net and store it in a variable in the functions ip_build_and_send_pkt and ip_queue_xmit so that it does not need to be recomputed next time it is needed. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 10366ee..a7012f2 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -139,6 +139,7 @@ int ip_build_and_send_pkt(struct sk_buff *skb, const struct sock *sk, { struct inet_sock *inet = inet_sk(sk); struct rtable *rt = skb_rtable(skb); + struct net *net = sock_net(sk); struct iphdr *iph; /* Build the IP header. */ @@ -157,7 +158,7 @@ int ip_build_and_send_pkt(struct sk_buff *skb, const struct sock *sk, iph->id = 0; } else { iph->frag_off = 0; - __ip_select_ident(sock_net(sk), iph, 1); + __ip_select_ident(net, iph, 1); } if (opt && opt->opt.optlen) { @@ -382,6 +383,7 @@ static void ip_copy_addrs(struct iphdr *iph, const struct flowi4 *fl4) int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl) { struct inet_sock *inet = inet_sk(sk); + struct net *net = sock_net(sk); struct ip_options_rcu *inet_opt; struct flowi4 *fl4; struct rtable *rt; @@ -412,7 +414,7 @@ int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl) * keep trying until route appears or the connection times * itself out. */ - rt = ip_route_output_ports(sock_net(sk), fl4, sk, + rt = ip_route_output_ports(net, fl4, sk, daddr, inet->inet_saddr, inet->inet_dport, inet->inet_sport, @@ -449,7 +451,7 @@ packet_routed: ip_options_build(skb, &inet_opt->opt, inet->inet_daddr, rt, 0); } - ip_select_ident_segs(sock_net(sk), skb, sk, + ip_select_ident_segs(net, skb, sk, skb_shinfo(skb)->gso_segs ?: 1); /* TODO : should we use skb->sk here instead of sk ? */ @@ -462,7 +464,7 @@ packet_routed: no_route: rcu_read_unlock(); - IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES); + IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES); kfree_skb(skb); return -EHOSTUNREACH; } -- cgit v0.10.2 From a7093fefa543afad2bf292bba163800e81a4ba33 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 7 Oct 2015 16:48:43 -0500 Subject: ppp: Cache net in pptp_xmit Compute net and store it in a variable in pptp_xmit, so that the value can be reused the next time it is needed. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c index 6bef7be..5243ab6 100644 --- a/drivers/net/ppp/pptp.c +++ b/drivers/net/ppp/pptp.c @@ -169,6 +169,7 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb) { struct sock *sk = (struct sock *) chan->private; struct pppox_sock *po = pppox_sk(sk); + struct net *net = sock_net(sk); struct pptp_opt *opt = &po->proto.pptp; struct pptp_gre_header *hdr; unsigned int header_len = sizeof(*hdr); @@ -187,7 +188,7 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb) if (sk_pppox(po)->sk_state & PPPOX_DEAD) goto tx_error; - rt = ip_route_output_ports(sock_net(sk), &fl4, NULL, + rt = ip_route_output_ports(net, &fl4, NULL, opt->dst_addr.sin_addr.s_addr, opt->src_addr.sin_addr.s_addr, 0, 0, IPPROTO_GRE, @@ -279,7 +280,7 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb) nf_reset(skb); skb->ip_summed = CHECKSUM_NONE; - ip_select_ident(sock_net(sk), skb, NULL); + ip_select_ident(net, skb, NULL); ip_send_check(iph); ip_local_out(skb->sk, skb); -- cgit v0.10.2 From 57c4bf859cad9d6c4f73d8c98a95e00f156301e0 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 7 Oct 2015 16:48:44 -0500 Subject: ipvlan: Cache net in ipvlan_process_v4_outbound and ipvlan_process_v6_outbound Compute net once in ipvlan_process_v4_outbound and ipvlan_process_v6_outbound and store it in a variable so that net does not need to be recomputed next time it is used. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c index 75dcf36..976f30b 100644 --- a/drivers/net/ipvlan/ipvlan_core.c +++ b/drivers/net/ipvlan/ipvlan_core.c @@ -344,6 +344,7 @@ static int ipvlan_process_v4_outbound(struct sk_buff *skb) { const struct iphdr *ip4h = ip_hdr(skb); struct net_device *dev = skb->dev; + struct net *net = dev_net(dev); struct rtable *rt; int err, ret = NET_XMIT_DROP; struct flowi4 fl4 = { @@ -354,7 +355,7 @@ static int ipvlan_process_v4_outbound(struct sk_buff *skb) .saddr = ip4h->saddr, }; - rt = ip_route_output_flow(dev_net(dev), &fl4, NULL); + rt = ip_route_output_flow(net, &fl4, NULL); if (IS_ERR(rt)) goto err; @@ -381,6 +382,7 @@ static int ipvlan_process_v6_outbound(struct sk_buff *skb) { const struct ipv6hdr *ip6h = ipv6_hdr(skb); struct net_device *dev = skb->dev; + struct net *net = dev_net(dev); struct dst_entry *dst; int err, ret = NET_XMIT_DROP; struct flowi6 fl6 = { @@ -393,7 +395,7 @@ static int ipvlan_process_v6_outbound(struct sk_buff *skb) .flowi6_proto = ip6h->nexthdr, }; - dst = ip6_route_output(dev_net(dev), NULL, &fl6); + dst = ip6_route_output(net, NULL, &fl6); if (dst->error) { ret = dst->error; dst_release(dst); -- cgit v0.10.2 From cf91a99daa4651d0c1f52b8c3d813fd44b43cada Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 7 Oct 2015 16:48:45 -0500 Subject: ipv4, ipv6: Pass net into __ip_local_out and __ip6_local_out Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 231f9d8..b27dc11 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -74,7 +74,7 @@ static struct dst_entry *vrf_ip_check(struct dst_entry *dst, u32 cookie) return dst; } -static int vrf_ip_local_out(struct sock *sk, struct sk_buff *skb) +static int vrf_ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb) { return ip_local_out(sk, skb); } diff --git a/include/net/dst_ops.h b/include/net/dst_ops.h index 3f26a6a..a0d443c 100644 --- a/include/net/dst_ops.h +++ b/include/net/dst_ops.h @@ -9,6 +9,7 @@ struct kmem_cachep; struct net_device; struct sk_buff; struct sock; +struct net; struct dst_ops { unsigned short family; @@ -28,7 +29,7 @@ struct dst_ops { struct sk_buff *skb, u32 mtu); void (*redirect)(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb); - int (*local_out)(struct sock *sk, struct sk_buff *skb); + int (*local_out)(struct net *net, struct sock *sk, struct sk_buff *skb); struct neighbour * (*neigh_lookup)(const struct dst_entry *dst, struct sk_buff *skb, const void *daddr); diff --git a/include/net/ip.h b/include/net/ip.h index 03e80f9..34b4038 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -112,7 +112,7 @@ int ip_mc_output(struct sock *sk, struct sk_buff *skb); int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, int (*output)(struct net *, struct sock *, struct sk_buff *)); void ip_send_check(struct iphdr *ip); -int __ip_local_out(struct sock *sk, struct sk_buff *skb); +int __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb); int ip_local_out(struct sock *sk, struct sk_buff *skb); int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl); diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 30eb182..4283403 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -865,7 +865,7 @@ int ip6_forward(struct sk_buff *skb); int ip6_input(struct sk_buff *skb); int ip6_mc_input(struct sk_buff *skb); -int __ip6_local_out(struct sock *sk, struct sk_buff *skb); +int __ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb); int ip6_local_out(struct sock *sk, struct sk_buff *skb); /* diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index a7012f2..39d3fbe 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -96,9 +96,8 @@ void ip_send_check(struct iphdr *iph) } EXPORT_SYMBOL(ip_send_check); -int __ip_local_out(struct sock *sk, struct sk_buff *skb) +int __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb) { - struct net *net = dev_net(skb_dst(skb)->dev); struct iphdr *iph = ip_hdr(skb); iph->tot_len = htons(skb->len); @@ -113,7 +112,7 @@ int ip_local_out(struct sock *sk, struct sk_buff *skb) struct net *net = dev_net(skb_dst(skb)->dev); int err; - err = __ip_local_out(sk, skb); + err = __ip_local_out(net, sk, skb); if (likely(err == 1)) err = dst_output(net, sk, skb); diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c index 1285581..7f64d67 100644 --- a/net/ipv6/output_core.c +++ b/net/ipv6/output_core.c @@ -138,9 +138,8 @@ int ip6_dst_hoplimit(struct dst_entry *dst) EXPORT_SYMBOL(ip6_dst_hoplimit); #endif -int __ip6_local_out(struct sock *sk, struct sk_buff *skb) +int __ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb) { - struct net *net = dev_net(skb_dst(skb)->dev); int len; len = skb->len - sizeof(struct ipv6hdr); @@ -160,7 +159,7 @@ int ip6_local_out(struct sock *sk, struct sk_buff *skb) struct net *net = dev_net(skb_dst(skb)->dev); int err; - err = __ip6_local_out(sk, skb); + err = __ip6_local_out(net, sk, skb); if (likely(err == 1)) err = dst_output(net, sk, skb); diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c index a7a254f..cc3676e 100644 --- a/net/xfrm/xfrm_output.c +++ b/net/xfrm/xfrm_output.c @@ -136,7 +136,7 @@ int xfrm_output_resume(struct sk_buff *skb, int err) while (likely((err = xfrm_output_one(skb, err)) == 0)) { nf_reset(skb); - err = skb_dst(skb)->ops->local_out(skb->sk, skb); + err = skb_dst(skb)->ops->local_out(net, skb->sk, skb); if (unlikely(err != 1)) goto out; -- cgit v0.10.2 From 33224b16ffccb49cf798317670389e0bfba0024c Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 7 Oct 2015 16:48:46 -0500 Subject: ipv4, ipv6: Pass net into ip_local_out and ip6_local_out Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c index 976f30b..24f8dbc 100644 --- a/drivers/net/ipvlan/ipvlan_core.c +++ b/drivers/net/ipvlan/ipvlan_core.c @@ -365,7 +365,7 @@ static int ipvlan_process_v4_outbound(struct sk_buff *skb) } skb_dst_drop(skb); skb_dst_set(skb, &rt->dst); - err = ip_local_out(skb->sk, skb); + err = ip_local_out(net, skb->sk, skb); if (unlikely(net_xmit_eval(err))) dev->stats.tx_errors++; else @@ -403,7 +403,7 @@ static int ipvlan_process_v6_outbound(struct sk_buff *skb) } skb_dst_drop(skb); skb_dst_set(skb, dst); - err = ip6_local_out(skb->sk, skb); + err = ip6_local_out(net, skb->sk, skb); if (unlikely(net_xmit_eval(err))) dev->stats.tx_errors++; else diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c index 5243ab6..fc69e41 100644 --- a/drivers/net/ppp/pptp.c +++ b/drivers/net/ppp/pptp.c @@ -283,7 +283,7 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb) ip_select_ident(net, skb, NULL); ip_send_check(iph); - ip_local_out(skb->sk, skb); + ip_local_out(net, skb->sk, skb); return 1; tx_error: diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index b27dc11..21bb7de 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -76,7 +76,7 @@ static struct dst_entry *vrf_ip_check(struct dst_entry *dst, u32 cookie) static int vrf_ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb) { - return ip_local_out(sk, skb); + return ip_local_out(net, sk, skb); } static unsigned int vrf_v4_mtu(const struct dst_entry *dst) @@ -222,7 +222,7 @@ static netdev_tx_t vrf_process_v4_outbound(struct sk_buff *skb, RT_SCOPE_LINK); } - ret = ip_local_out(skb->sk, skb); + ret = ip_local_out(dev_net(skb_dst(skb)->dev), skb->sk, skb); if (unlikely(net_xmit_eval(ret))) vrf_dev->stats.tx_errors++; else diff --git a/include/net/ip.h b/include/net/ip.h index 34b4038..7febbab 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -113,7 +113,7 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, int (*output)(struct net *, struct sock *, struct sk_buff *)); void ip_send_check(struct iphdr *ip); int __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb); -int ip_local_out(struct sock *sk, struct sk_buff *skb); +int ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb); int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl); void ip_init(void); diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h index 8f18a8b..aaee6fa 100644 --- a/include/net/ip6_tunnel.h +++ b/include/net/ip6_tunnel.h @@ -87,7 +87,7 @@ static inline void ip6tunnel_xmit(struct sock *sk, struct sk_buff *skb, int pkt_len, err; pkt_len = skb->len - skb_inner_network_offset(skb); - err = ip6_local_out(sk, skb); + err = ip6_local_out(dev_net(skb_dst(skb)->dev), sk, skb); if (net_xmit_eval(err) == 0) { struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats); diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 4283403..fce8120 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -866,7 +866,7 @@ int ip6_input(struct sk_buff *skb); int ip6_mc_input(struct sk_buff *skb); int __ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb); -int ip6_local_out(struct sock *sk, struct sk_buff *skb); +int ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb); /* * Extension header (options) processing diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 43375d9..64aaf35 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -397,7 +397,7 @@ static int igmpv3_sendpack(struct sk_buff *skb) pig->csum = ip_compute_csum(igmp_hdr(skb), igmplen); - return ip_local_out(skb->sk, skb); + return ip_local_out(dev_net(skb_dst(skb)->dev), skb->sk, skb); } static int grec_size(struct ip_mc_list *pmc, int type, int gdel, int sdel) @@ -739,7 +739,7 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc, ih->group = group; ih->csum = ip_compute_csum((void *)ih, sizeof(struct igmphdr)); - return ip_local_out(skb->sk, skb); + return ip_local_out(net, skb->sk, skb); } static void igmp_gq_timer_expire(unsigned long data) diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 39d3fbe..9fe100a 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -107,9 +107,8 @@ int __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb) dst_output); } -int ip_local_out(struct sock *sk, struct sk_buff *skb) +int ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb) { - struct net *net = dev_net(skb_dst(skb)->dev); int err; err = __ip_local_out(net, sk, skb); @@ -169,7 +168,7 @@ int ip_build_and_send_pkt(struct sk_buff *skb, const struct sock *sk, skb->mark = sk->sk_mark; /* Send it out. */ - return ip_local_out(skb->sk, skb); + return ip_local_out(net, skb->sk, skb); } EXPORT_SYMBOL_GPL(ip_build_and_send_pkt); @@ -457,7 +456,7 @@ packet_routed: skb->priority = sk->sk_priority; skb->mark = sk->sk_mark; - res = ip_local_out(sk, skb); + res = ip_local_out(net, sk, skb); rcu_read_unlock(); return res; @@ -1437,7 +1436,7 @@ int ip_send_skb(struct net *net, struct sk_buff *skb) { int err; - err = ip_local_out(skb->sk, skb); + err = ip_local_out(net, skb->sk, skb); if (err) { if (err > 0) err = net_xmit_errno(err); diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c index caef8e2..6cb9009 100644 --- a/net/ipv4/ip_tunnel_core.c +++ b/net/ipv4/ip_tunnel_core.c @@ -79,7 +79,7 @@ int iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb, iph->ttl = ttl; __ip_select_ident(net, iph, skb_shinfo(skb)->gso_segs ?: 1); - err = ip_local_out(sk, skb); + err = ip_local_out(net, sk, skb); if (unlikely(net_xmit_eval(err))) pkt_len = 0; return pkt_len; diff --git a/net/ipv4/netfilter/ipt_SYNPROXY.c b/net/ipv4/netfilter/ipt_SYNPROXY.c index 473faf7..f1a8df8 100644 --- a/net/ipv4/netfilter/ipt_SYNPROXY.c +++ b/net/ipv4/netfilter/ipt_SYNPROXY.c @@ -63,7 +63,7 @@ synproxy_send_tcp(const struct synproxy_net *snet, nf_conntrack_get(nfct); } - ip_local_out(nskb->sk, nskb); + ip_local_out(net, nskb->sk, nskb); return; free_nskb: diff --git a/net/ipv4/netfilter/nf_dup_ipv4.c b/net/ipv4/netfilter/nf_dup_ipv4.c index 0b9abfb..ceb1873 100644 --- a/net/ipv4/netfilter/nf_dup_ipv4.c +++ b/net/ipv4/netfilter/nf_dup_ipv4.c @@ -92,7 +92,7 @@ void nf_dup_ipv4(struct net *net, struct sk_buff *skb, unsigned int hooknum, if (nf_dup_ipv4_route(net, skb, gw, oif)) { __this_cpu_write(nf_skb_duplicated, true); - ip_local_out(skb->sk, skb); + ip_local_out(net, skb->sk, skb); __this_cpu_write(nf_skb_duplicated, false); } else { kfree_skb(skb); diff --git a/net/ipv4/netfilter/nf_reject_ipv4.c b/net/ipv4/netfilter/nf_reject_ipv4.c index dcc125c..c747b2d 100644 --- a/net/ipv4/netfilter/nf_reject_ipv4.c +++ b/net/ipv4/netfilter/nf_reject_ipv4.c @@ -157,7 +157,7 @@ void nf_send_reset(struct net *net, struct sk_buff *oldskb, int hook) dev_queue_xmit(nskb); } else #endif - ip_local_out(nskb->sk, nskb); + ip_local_out(net, nskb->sk, nskb); return; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 31c686b..98510fa 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1692,7 +1692,7 @@ int ip6_send_skb(struct sk_buff *skb) struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); int err; - err = ip6_local_out(skb->sk, skb); + err = ip6_local_out(net, skb->sk, skb); if (err) { if (err > 0) err = net_xmit_errno(err); diff --git a/net/ipv6/netfilter/ip6t_SYNPROXY.c b/net/ipv6/netfilter/ip6t_SYNPROXY.c index c38c341..a10a2a9 100644 --- a/net/ipv6/netfilter/ip6t_SYNPROXY.c +++ b/net/ipv6/netfilter/ip6t_SYNPROXY.c @@ -76,7 +76,7 @@ synproxy_send_tcp(const struct synproxy_net *snet, nf_conntrack_get(nfct); } - ip6_local_out(nskb->sk, nskb); + ip6_local_out(net, nskb->sk, nskb); return; free_nskb: diff --git a/net/ipv6/netfilter/nf_dup_ipv6.c b/net/ipv6/netfilter/nf_dup_ipv6.c index 64f3fe5..6989c70 100644 --- a/net/ipv6/netfilter/nf_dup_ipv6.c +++ b/net/ipv6/netfilter/nf_dup_ipv6.c @@ -68,7 +68,7 @@ void nf_dup_ipv6(struct net *net, struct sk_buff *skb, unsigned int hooknum, } if (nf_dup_ipv6_route(net, skb, gw, oif)) { __this_cpu_write(nf_skb_duplicated, true); - ip6_local_out(skb->sk, skb); + ip6_local_out(net, skb->sk, skb); __this_cpu_write(nf_skb_duplicated, false); } else { kfree_skb(skb); diff --git a/net/ipv6/netfilter/nf_reject_ipv6.c b/net/ipv6/netfilter/nf_reject_ipv6.c index a4f73e2..7309e47 100644 --- a/net/ipv6/netfilter/nf_reject_ipv6.c +++ b/net/ipv6/netfilter/nf_reject_ipv6.c @@ -206,7 +206,7 @@ void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook) dev_queue_xmit(nskb); } else #endif - ip6_local_out(nskb->sk, nskb); + ip6_local_out(net, nskb->sk, nskb); } EXPORT_SYMBOL_GPL(nf_send_reset6); diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c index 7f64d67..462f2a76b 100644 --- a/net/ipv6/output_core.c +++ b/net/ipv6/output_core.c @@ -154,9 +154,8 @@ int __ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb) } EXPORT_SYMBOL_GPL(__ip6_local_out); -int ip6_local_out(struct sock *sk, struct sk_buff *skb) +int ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb) { - struct net *net = dev_net(skb_dst(skb)->dev); int err; err = __ip6_local_out(net, sk, skb); diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 2042b93..3264cb49 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -1049,7 +1049,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, ret = ip_vs_tunnel_xmit_prepare(skb, cp); if (ret == NF_ACCEPT) - ip_local_out(skb->sk, skb); + ip_local_out(net, skb->sk, skb); else if (ret == NF_DROP) kfree_skb(skb); rcu_read_unlock(); @@ -1141,7 +1141,7 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ret = ip_vs_tunnel_xmit_prepare(skb, cp); if (ret == NF_ACCEPT) - ip6_local_out(skb->sk, skb); + ip6_local_out(cp->ipvs->net, skb->sk, skb); else if (ret == NF_DROP) kfree_skb(skb); rcu_read_unlock(); -- cgit v0.10.2 From ede2059dbaf9c6557a49d466c8c7778343b208ff Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 7 Oct 2015 16:48:47 -0500 Subject: dst: Pass net into dst->output The network namespace is already passed into dst_output pass it into dst->output lwt->output and friends. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 21bb7de..191579a 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -312,10 +312,9 @@ err: return ret; } -static int vrf_output(struct sock *sk, struct sk_buff *skb) +static int vrf_output(struct net *net, struct sock *sk, struct sk_buff *skb) { struct net_device *dev = skb_dst(skb)->dev; - struct net *net = dev_net(dev); IP_UPD_PO_STATS(net, IPSTATS_MIB_OUT, skb->len); diff --git a/include/net/dst.h b/include/net/dst.h index fdd01fe..1279f9b 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -45,7 +45,7 @@ struct dst_entry { void *__pad1; #endif int (*input)(struct sk_buff *); - int (*output)(struct sock *sk, struct sk_buff *skb); + int (*output)(struct net *net, struct sock *sk, struct sk_buff *skb); unsigned short flags; #define DST_HOST 0x0001 @@ -365,10 +365,10 @@ static inline void skb_tunnel_rx(struct sk_buff *skb, struct net_device *dev, __skb_tunnel_rx(skb, dev, net); } -int dst_discard_sk(struct sock *sk, struct sk_buff *skb); +int dst_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb); static inline int dst_discard(struct sk_buff *skb) { - return dst_discard_sk(skb->sk, skb); + return dst_discard_out(&init_net, skb->sk, skb); } void *dst_alloc(struct dst_ops *ops, struct net_device *dev, int initial_ref, int initial_obsolete, unsigned short flags); @@ -456,7 +456,7 @@ static inline void dst_set_expires(struct dst_entry *dst, int timeout) /* Output packet to network from transport. */ static inline int dst_output(struct net *net, struct sock *sk, struct sk_buff *skb) { - return skb_dst(skb)->output(sk, skb); + return skb_dst(skb)->output(net, sk, skb); } /* Input packet from network to transport. */ diff --git a/include/net/ip.h b/include/net/ip.h index 7febbab..3c904a2 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -107,8 +107,8 @@ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev); int ip_local_deliver(struct sk_buff *skb); int ip_mr_input(struct sk_buff *skb); -int ip_output(struct sock *sk, struct sk_buff *skb); -int ip_mc_output(struct sock *sk, struct sk_buff *skb); +int ip_output(struct net *net, struct sock *sk, struct sk_buff *skb); +int ip_mc_output(struct net *net, struct sock *sk, struct sk_buff *skb); int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, int (*output)(struct net *, struct sock *, struct sk_buff *)); void ip_send_check(struct iphdr *ip); diff --git a/include/net/ipv6.h b/include/net/ipv6.h index fce8120..e1a10b0 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -860,7 +860,7 @@ struct dst_entry *ip6_blackhole_route(struct net *net, * skb processing functions */ -int ip6_output(struct sock *sk, struct sk_buff *skb); +int ip6_output(struct net *net, struct sock *sk, struct sk_buff *skb); int ip6_forward(struct sk_buff *skb); int ip6_input(struct sk_buff *skb); int ip6_mc_input(struct sk_buff *skb); diff --git a/include/net/lwtunnel.h b/include/net/lwtunnel.h index fce0e35..66350ce 100644 --- a/include/net/lwtunnel.h +++ b/include/net/lwtunnel.h @@ -18,7 +18,7 @@ struct lwtunnel_state { __u16 type; __u16 flags; atomic_t refcnt; - int (*orig_output)(struct sock *sk, struct sk_buff *skb); + int (*orig_output)(struct net *net, struct sock *sk, struct sk_buff *skb); int (*orig_input)(struct sk_buff *); int len; __u8 data[0]; @@ -28,7 +28,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); - int (*output)(struct sock *sk, struct sk_buff *skb); + 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, struct lwtunnel_state *lwtstate); @@ -88,7 +88,7 @@ int lwtunnel_fill_encap(struct sk_buff *skb, int lwtunnel_get_encap_size(struct lwtunnel_state *lwtstate); struct lwtunnel_state *lwtunnel_state_alloc(int hdr_len); int lwtunnel_cmp_encap(struct lwtunnel_state *a, struct lwtunnel_state *b); -int lwtunnel_output(struct sock *sk, struct sk_buff *skb); +int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb); int lwtunnel_input(struct sk_buff *skb); #else @@ -160,7 +160,7 @@ static inline int lwtunnel_cmp_encap(struct lwtunnel_state *a, return 0; } -static inline int lwtunnel_output(struct sock *sk, struct sk_buff *skb) +static inline int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb) { return -EOPNOTSUPP; } diff --git a/include/net/xfrm.h b/include/net/xfrm.h index fd17610..4a9c21f 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -333,7 +333,7 @@ struct xfrm_state_afinfo { const xfrm_address_t *saddr); int (*tmpl_sort)(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n); int (*state_sort)(struct xfrm_state **dst, struct xfrm_state **src, int n); - int (*output)(struct sock *sk, struct sk_buff *skb); + int (*output)(struct net *net, struct sock *sk, struct sk_buff *skb); int (*output_finish)(struct sock *sk, struct sk_buff *skb); int (*extract_input)(struct xfrm_state *x, struct sk_buff *skb); @@ -1527,7 +1527,7 @@ static inline int xfrm4_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi) int xfrm4_extract_output(struct xfrm_state *x, struct sk_buff *skb); int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb); -int xfrm4_output(struct sock *sk, struct sk_buff *skb); +int xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb); int xfrm4_output_finish(struct sock *sk, struct sk_buff *skb); int xfrm4_rcv_cb(struct sk_buff *skb, u8 protocol, int err); int xfrm4_protocol_register(struct xfrm4_protocol *handler, unsigned char protocol); @@ -1552,7 +1552,7 @@ __be32 xfrm6_tunnel_alloc_spi(struct net *net, xfrm_address_t *saddr); __be32 xfrm6_tunnel_spi_lookup(struct net *net, const xfrm_address_t *saddr); int xfrm6_extract_output(struct xfrm_state *x, struct sk_buff *skb); int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb); -int xfrm6_output(struct sock *sk, struct sk_buff *skb); +int xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb); int xfrm6_output_finish(struct sock *sk, struct sk_buff *skb); int xfrm6_find_1stfragopt(struct xfrm_state *x, struct sk_buff *skb, u8 **prevhdr); diff --git a/net/core/dst.c b/net/core/dst.c index 0771c8c..2a18180 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -144,12 +144,12 @@ loop: mutex_unlock(&dst_gc_mutex); } -int dst_discard_sk(struct sock *sk, struct sk_buff *skb) +int dst_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb) { kfree_skb(skb); return 0; } -EXPORT_SYMBOL(dst_discard_sk); +EXPORT_SYMBOL(dst_discard_out); const u32 dst_default_metrics[RTAX_MAX + 1] = { /* This initializer is needed to force linker to place this variable @@ -177,7 +177,7 @@ void dst_init(struct dst_entry *dst, struct dst_ops *ops, dst->xfrm = NULL; #endif dst->input = dst_discard; - dst->output = dst_discard_sk; + dst->output = dst_discard_out; dst->error = 0; dst->obsolete = initial_obsolete; dst->header_len = 0; @@ -224,7 +224,7 @@ static void ___dst_free(struct dst_entry *dst) */ if (dst->dev == NULL || !(dst->dev->flags&IFF_UP)) { dst->input = dst_discard; - dst->output = dst_discard_sk; + dst->output = dst_discard_out; } dst->obsolete = DST_OBSOLETE_DEAD; } @@ -352,7 +352,7 @@ static struct dst_ops md_dst_ops = { .family = AF_UNSPEC, }; -static int dst_md_discard_sk(struct sock *sk, struct sk_buff *skb) +static int dst_md_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb) { WARN_ONCE(1, "Attempting to call output on metadata dst\n"); kfree_skb(skb); @@ -375,7 +375,7 @@ static void __metadata_dst_init(struct metadata_dst *md_dst, u8 optslen) DST_METADATA | DST_NOCACHE | DST_NOCOUNT); dst->input = dst_md_discard; - dst->output = dst_md_discard_sk; + dst->output = dst_md_discard_out; memset(dst + 1, 0, sizeof(*md_dst) + optslen - sizeof(*dst)); } @@ -430,7 +430,7 @@ static void dst_ifdown(struct dst_entry *dst, struct net_device *dev, if (!unregister) { dst->input = dst_discard; - dst->output = dst_discard_sk; + dst->output = dst_discard_out; } else { dst->dev = dev_net(dst->dev)->loopback_dev; dev_hold(dst->dev); diff --git a/net/core/lwtunnel.c b/net/core/lwtunnel.c index dfb1a9c..299cfc2 100644 --- a/net/core/lwtunnel.c +++ b/net/core/lwtunnel.c @@ -180,7 +180,7 @@ int lwtunnel_cmp_encap(struct lwtunnel_state *a, struct lwtunnel_state *b) } EXPORT_SYMBOL(lwtunnel_cmp_encap); -int lwtunnel_output(struct sock *sk, struct sk_buff *skb) +int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); const struct lwtunnel_encap_ops *ops; @@ -199,7 +199,7 @@ int lwtunnel_output(struct sock *sk, struct sk_buff *skb) rcu_read_lock(); ops = rcu_dereference(lwtun_encaps[lwtstate->type]); if (likely(ops && ops->output)) - ret = ops->output(sk, skb); + ret = ops->output(net, sk, skb); rcu_read_unlock(); if (ret == -EOPNOTSUPP) diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index e930321..27fce28 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -744,7 +744,7 @@ out: return NET_RX_DROP; } -static int dn_output(struct sock *sk, struct sk_buff *skb) +static int dn_output(struct net *net, struct sock *sk, struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); struct dn_route *rt = (struct dn_route *)dst; @@ -832,7 +832,7 @@ drop: * Used to catch bugs. This should never normally get * called. */ -static int dn_rt_bug_sk(struct sock *sk, struct sk_buff *skb) +static int dn_rt_bug_out(struct net *net, struct sock *sk, struct sk_buff *skb) { struct dn_skb_cb *cb = DN_SKB_CB(skb); @@ -1469,7 +1469,7 @@ make_route: rt->n = neigh; rt->dst.lastuse = jiffies; - rt->dst.output = dn_rt_bug_sk; + rt->dst.output = dn_rt_bug_out; switch (res.type) { case RTN_UNICAST: rt->dst.input = dn_forward; diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 9fe100a..67404e1 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -284,11 +284,10 @@ static int ip_finish_output(struct net *net, struct sock *sk, struct sk_buff *sk return ip_finish_output2(net, sk, skb); } -int ip_mc_output(struct sock *sk, struct sk_buff *skb) +int ip_mc_output(struct net *net, struct sock *sk, struct sk_buff *skb) { struct rtable *rt = skb_rtable(skb); struct net_device *dev = rt->dst.dev; - struct net *net = dev_net(dev); /* * If the indicated interface is up and running, send the packet. @@ -347,10 +346,9 @@ int ip_mc_output(struct sock *sk, struct sk_buff *skb) !(IPCB(skb)->flags & IPSKB_REROUTED)); } -int ip_output(struct sock *sk, struct sk_buff *skb) +int ip_output(struct net *net, struct sock *sk, struct sk_buff *skb) { struct net_device *dev = skb_dst(skb)->dev; - struct net *net = dev_net(dev); IP_UPD_PO_STATS(net, IPSTATS_MIB_OUT, skb->len); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index bf1486b..4be5ff08 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1152,7 +1152,7 @@ static void ipv4_link_failure(struct sk_buff *skb) dst_set_expires(&rt->dst, 0); } -static int ip_rt_bug(struct sock *sk, struct sk_buff *skb) +static int ip_rt_bug(struct net *net, struct sock *sk, struct sk_buff *skb) { pr_debug("%s: %pI4 -> %pI4, %s\n", __func__, &ip_hdr(skb)->saddr, &ip_hdr(skb)->daddr, @@ -2303,7 +2303,7 @@ struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_or new->__use = 1; new->input = dst_discard; - new->output = dst_discard_sk; + new->output = dst_discard_out; new->dev = ort->dst.dev; if (new->dev) diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c index 17db61f..9f298d0 100644 --- a/net/ipv4/xfrm4_output.c +++ b/net/ipv4/xfrm4_output.c @@ -94,10 +94,8 @@ static int __xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb) return x->outer_mode->afinfo->output_finish(sk, skb); } -int xfrm4_output(struct sock *sk, struct sk_buff *skb) +int xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb) { - struct net *net = dev_net(skb_dst(skb)->dev); - return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, net, sk, skb, NULL, skb_dst(skb)->dev, __xfrm4_output, diff --git a/net/ipv6/ila.c b/net/ipv6/ila.c index 678d2df..1a6852e 100644 --- a/net/ipv6/ila.c +++ b/net/ipv6/ila.c @@ -91,7 +91,7 @@ static void update_ipv6_locator(struct sk_buff *skb, struct ila_params *p) *(__be64 *)&ip6h->daddr = p->locator; } -static int ila_output(struct sock *sk, struct sk_buff *skb) +static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); @@ -100,7 +100,7 @@ static int ila_output(struct sock *sk, struct sk_buff *skb) update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate)); - return dst->lwtstate->orig_output(sk, skb); + return dst->lwtstate->orig_output(net, sk, skb); drop: kfree_skb(skb); diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 98510fa..32583b5 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -130,11 +130,10 @@ static int ip6_finish_output(struct net *net, struct sock *sk, struct sk_buff *s return ip6_finish_output2(net, sk, skb); } -int ip6_output(struct sock *sk, struct sk_buff *skb) +int ip6_output(struct net *net, struct sock *sk, struct sk_buff *skb) { struct net_device *dev = skb_dst(skb)->dev; struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb)); - struct net *net = dev_net(dev); if (unlikely(idev->cnf.disable_ipv6)) { IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS); diff --git a/net/ipv6/route.c b/net/ipv6/route.c index d3d9467..4320ddc 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -86,9 +86,9 @@ static void ip6_dst_ifdown(struct dst_entry *, static int ip6_dst_gc(struct dst_ops *ops); static int ip6_pkt_discard(struct sk_buff *skb); -static int ip6_pkt_discard_out(struct sock *sk, struct sk_buff *skb); +static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb); static int ip6_pkt_prohibit(struct sk_buff *skb); -static int ip6_pkt_prohibit_out(struct sock *sk, struct sk_buff *skb); +static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb); static void ip6_link_failure(struct sk_buff *skb); static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb, u32 mtu); @@ -308,7 +308,7 @@ static const struct rt6_info ip6_blk_hole_entry_template = { .obsolete = DST_OBSOLETE_FORCE_CHK, .error = -EINVAL, .input = dst_discard, - .output = dst_discard_sk, + .output = dst_discard_out, }, .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), .rt6i_protocol = RTPROT_KERNEL, @@ -1195,7 +1195,7 @@ struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_ori new->__use = 1; new->input = dst_discard; - new->output = dst_discard_sk; + new->output = dst_discard_out; if (dst_metrics_read_only(&ort->dst)) new->_metrics = ort->dst._metrics; @@ -1853,7 +1853,7 @@ int ip6_route_info_create(struct fib6_config *cfg, struct rt6_info **rt_ret) switch (cfg->fc_type) { case RTN_BLACKHOLE: rt->dst.error = -EINVAL; - rt->dst.output = dst_discard_sk; + rt->dst.output = dst_discard_out; rt->dst.input = dst_discard; break; case RTN_PROHIBIT: @@ -2446,7 +2446,7 @@ static int ip6_pkt_discard(struct sk_buff *skb) return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES); } -static int ip6_pkt_discard_out(struct sock *sk, struct sk_buff *skb) +static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb) { skb->dev = skb_dst(skb)->dev; return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES); @@ -2457,7 +2457,7 @@ static int ip6_pkt_prohibit(struct sk_buff *skb) return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES); } -static int ip6_pkt_prohibit_out(struct sock *sk, struct sk_buff *skb) +static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb) { skb->dev = skb_dst(skb)->dev; return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c index c9a5bd5..9db067a 100644 --- a/net/ipv6/xfrm6_output.c +++ b/net/ipv6/xfrm6_output.c @@ -173,10 +173,8 @@ static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb) return x->outer_mode->afinfo->output_finish(sk, skb); } -int xfrm6_output(struct sock *sk, struct sk_buff *skb) +int xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb) { - struct net *net = dev_net(skb_dst(skb)->dev); - return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, net, sk, skb, NULL, skb_dst(skb)->dev, __xfrm6_output, diff --git a/net/mpls/mpls_iptunnel.c b/net/mpls/mpls_iptunnel.c index 21e70bc..67591ae 100644 --- a/net/mpls/mpls_iptunnel.c +++ b/net/mpls/mpls_iptunnel.c @@ -37,7 +37,7 @@ static unsigned int mpls_encap_size(struct mpls_iptunnel_encap *en) return en->labels * sizeof(struct mpls_shim_hdr); } -int mpls_output(struct sock *sk, struct sk_buff *skb) +int mpls_output(struct net *net, struct sock *sk, struct sk_buff *skb) { struct mpls_iptunnel_encap *tun_encap_info; struct mpls_shim_hdr *hdr; diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index f4f2d98..09bfcba 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1957,7 +1957,7 @@ purge_queue: xfrm_pol_put(pol); } -static int xdst_queue_output(struct sock *sk, struct sk_buff *skb) +static int xdst_queue_output(struct net *net, struct sock *sk, struct sk_buff *skb) { unsigned long sched_next; struct dst_entry *dst = skb_dst(skb); -- cgit v0.10.2 From bb257c3813613ce11329568c21c200e95afe6fc1 Mon Sep 17 00:00:00 2001 From: Arun Parameswaran Date: Tue, 6 Oct 2015 12:25:46 -0700 Subject: dt-bindings: net: Broadcom iProc MDIO bus driver device tree binding Add device tree binding documentation for the Broadcom iProc MDIO bus driver. Signed-off-by: Arun Parameswaran Signed-off-by: David S. Miller diff --git a/Documentation/devicetree/bindings/net/brcm,iproc-mdio.txt b/Documentation/devicetree/bindings/net/brcm,iproc-mdio.txt new file mode 100644 index 0000000..8ba9ed1 --- /dev/null +++ b/Documentation/devicetree/bindings/net/brcm,iproc-mdio.txt @@ -0,0 +1,23 @@ +* Broadcom iProc MDIO bus controller + +Required properties: +- compatible: should be "brcm,iproc-mdio" +- reg: address and length of the register set for the MDIO interface +- #size-cells: must be 1 +- #address-cells: must be 0 + +Child nodes of this MDIO bus controller node are standard Ethernet PHY device +nodes as described in Documentation/devicetree/bindings/net/phy.txt + +Example: + +mdio@18002000 { + compatible = "brcm,iproc-mdio"; + reg = <0x18002000 0x8>; + #size-cells = <1>; + #address-cells = <0>; + + enet-gphy@0 { + reg = <0>; + }; +}; -- cgit v0.10.2 From ddc24ae1fd6ae2638365b0b824750b5b2ef13ca5 Mon Sep 17 00:00:00 2001 From: Arun Parameswaran Date: Tue, 6 Oct 2015 12:25:47 -0700 Subject: net: phy: Broadcom iProc MDIO bus driver This patch adds support for the Broadcom iProc MDIO bus interface. The MDIO interface can be found in the Broadcom iProc family Soc's. The MDIO bus is accessed using a combination of command and data registers. This MDIO driver provides access to the Etherent GPHY's connected to the MDIO bus. Signed-off-by: Arun Parameswaran Signed-off-by: David S. Miller diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index c5ad98a..b57f6c2 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -225,6 +225,15 @@ config MDIO_BCM_UNIMAC This hardware can be found in the Broadcom GENET Ethernet MAC controllers as well as some Broadcom Ethernet switches such as the Starfighter 2 switches. + +config MDIO_BCM_IPROC + tristate "Broadcom iProc MDIO bus controller" + depends on ARCH_BCM_IPROC || COMPILE_TEST + depends on HAS_IOMEM && OF_MDIO + help + This module provides a driver for the MDIO busses found in the + Broadcom iProc SoC's. + endif # PHYLIB config MICREL_KS8995MA diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 87f079c..f4e6eb9 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -38,3 +38,4 @@ obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o obj-$(CONFIG_MDIO_BCM_UNIMAC) += mdio-bcm-unimac.o obj-$(CONFIG_MICROCHIP_PHY) += microchip.o +obj-$(CONFIG_MDIO_BCM_IPROC) += mdio-bcm-iproc.o diff --git a/drivers/net/phy/mdio-bcm-iproc.c b/drivers/net/phy/mdio-bcm-iproc.c new file mode 100644 index 0000000..c0b4e65 --- /dev/null +++ b/drivers/net/phy/mdio-bcm-iproc.c @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2015 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; 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 + +#define IPROC_GPHY_MDCDIV 0x1a + +#define MII_CTRL_OFFSET 0x000 + +#define MII_CTRL_DIV_SHIFT 0 +#define MII_CTRL_PRE_SHIFT 7 +#define MII_CTRL_BUSY_SHIFT 8 + +#define MII_DATA_OFFSET 0x004 +#define MII_DATA_MASK 0xffff +#define MII_DATA_TA_SHIFT 16 +#define MII_DATA_TA_VAL 2 +#define MII_DATA_RA_SHIFT 18 +#define MII_DATA_PA_SHIFT 23 +#define MII_DATA_OP_SHIFT 28 +#define MII_DATA_OP_WRITE 1 +#define MII_DATA_OP_READ 2 +#define MII_DATA_SB_SHIFT 30 + +struct iproc_mdio_priv { + struct mii_bus *mii_bus; + void __iomem *base; +}; + +static inline int iproc_mdio_wait_for_idle(void __iomem *base) +{ + u32 val; + unsigned int timeout = 1000; /* loop for 1s */ + + do { + val = readl(base + MII_CTRL_OFFSET); + if ((val & BIT(MII_CTRL_BUSY_SHIFT)) == 0) + return 0; + + usleep_range(1000, 2000); + } while (timeout--); + + return -ETIMEDOUT; +} + +static inline void iproc_mdio_config_clk(void __iomem *base) +{ + u32 val; + + val = (IPROC_GPHY_MDCDIV << MII_CTRL_DIV_SHIFT) | + BIT(MII_CTRL_PRE_SHIFT); + writel(val, base + MII_CTRL_OFFSET); +} + +static int iproc_mdio_read(struct mii_bus *bus, int phy_id, int reg) +{ + struct iproc_mdio_priv *priv = bus->priv; + u32 cmd; + int rc; + + rc = iproc_mdio_wait_for_idle(priv->base); + if (rc) + return rc; + + iproc_mdio_config_clk(priv->base); + + /* Prepare the read operation */ + cmd = (MII_DATA_TA_VAL << MII_DATA_TA_SHIFT) | + (reg << MII_DATA_RA_SHIFT) | + (phy_id << MII_DATA_PA_SHIFT) | + BIT(MII_DATA_SB_SHIFT) | + (MII_DATA_OP_READ << MII_DATA_OP_SHIFT); + + writel(cmd, priv->base + MII_DATA_OFFSET); + + rc = iproc_mdio_wait_for_idle(priv->base); + if (rc) + return rc; + + cmd = readl(priv->base + MII_DATA_OFFSET) & MII_DATA_MASK; + + return cmd; +} + +static int iproc_mdio_write(struct mii_bus *bus, int phy_id, + int reg, u16 val) +{ + struct iproc_mdio_priv *priv = bus->priv; + u32 cmd; + int rc; + + rc = iproc_mdio_wait_for_idle(priv->base); + if (rc) + return rc; + + iproc_mdio_config_clk(priv->base); + + /* Prepare the write operation */ + cmd = (MII_DATA_TA_VAL << MII_DATA_TA_SHIFT) | + (reg << MII_DATA_RA_SHIFT) | + (phy_id << MII_DATA_PA_SHIFT) | + BIT(MII_DATA_SB_SHIFT) | + (MII_DATA_OP_WRITE << MII_DATA_OP_SHIFT) | + ((u32)(val) & MII_DATA_MASK); + + writel(cmd, priv->base + MII_DATA_OFFSET); + + rc = iproc_mdio_wait_for_idle(priv->base); + if (rc) + return rc; + + return 0; +} + +static int iproc_mdio_probe(struct platform_device *pdev) +{ + struct iproc_mdio_priv *priv; + struct mii_bus *bus; + struct resource *res; + int rc; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) { + dev_err(&pdev->dev, "failed to ioremap register\n"); + return PTR_ERR(priv->base); + } + + priv->mii_bus = mdiobus_alloc(); + if (!priv->mii_bus) { + dev_err(&pdev->dev, "MDIO bus alloc failed\n"); + return -ENOMEM; + } + + bus = priv->mii_bus; + bus->priv = priv; + bus->name = "iProc MDIO bus"; + snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id); + bus->parent = &pdev->dev; + bus->read = iproc_mdio_read; + bus->write = iproc_mdio_write; + + rc = of_mdiobus_register(bus, pdev->dev.of_node); + if (rc) { + dev_err(&pdev->dev, "MDIO bus registration failed\n"); + goto err_iproc_mdio; + } + + platform_set_drvdata(pdev, priv); + + dev_info(&pdev->dev, "Broadcom iProc MDIO bus at 0x%p\n", priv->base); + + return 0; + +err_iproc_mdio: + mdiobus_free(bus); + return rc; +} + +static int iproc_mdio_remove(struct platform_device *pdev) +{ + struct iproc_mdio_priv *priv = platform_get_drvdata(pdev); + + mdiobus_unregister(priv->mii_bus); + mdiobus_free(priv->mii_bus); + + return 0; +} + +static const struct of_device_id iproc_mdio_of_match[] = { + { .compatible = "brcm,iproc-mdio", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, iproc_mdio_of_match); + +static struct platform_driver iproc_mdio_driver = { + .driver = { + .name = "iproc-mdio", + .of_match_table = iproc_mdio_of_match, + }, + .probe = iproc_mdio_probe, + .remove = iproc_mdio_remove, +}; + +module_platform_driver(iproc_mdio_driver); + +MODULE_AUTHOR("Broadcom Corporation"); +MODULE_DESCRIPTION("Broadcom iProc MDIO bus controller"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:iproc-mdio"); -- cgit v0.10.2 From a1cba5613edf50c2a213fa90c30aa10500b241b7 Mon Sep 17 00:00:00 2001 From: Arun Parameswaran Date: Tue, 6 Oct 2015 12:25:48 -0700 Subject: net: phy: Add Broadcom phy library for common interfaces This patch adds the Broadcom phy library to consolidate common interfaces shared by Broadcom phy's. Moved the common interfaces to the 'bcm-phy-lib.c' and updated the Broadcom PHY drivers to use the new APIs. Signed-off-by: Arun Parameswaran Signed-off-by: David S. Miller diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index b57f6c2..606fdc9 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -69,8 +69,12 @@ config SMSC_PHY ---help--- Currently supports the LAN83C185, LAN8187 and LAN8700 PHYs +config BCM_NET_PHYLIB + tristate + config BROADCOM_PHY tristate "Drivers for Broadcom PHYs" + select BCM_NET_PHYLIB ---help--- Currently supports the BCM5411, BCM5421, BCM5461, BCM54616S, BCM5464, BCM5481 and BCM5482 PHYs. @@ -78,11 +82,13 @@ config BROADCOM_PHY config BCM63XX_PHY tristate "Drivers for Broadcom 63xx SOCs internal PHY" depends on BCM63XX + select BCM_NET_PHYLIB ---help--- Currently supports the 6348 and 6358 PHYs. config BCM7XXX_PHY tristate "Drivers for Broadcom 7xxx SOCs internal PHYs" + select BCM_NET_PHYLIB ---help--- Currently supports the BCM7366, BCM7439, BCM7445, and 40nm and 65nm generation of BCM7xxx Set Top Box SoCs. diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index f4e6eb9..6932475 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_QSEMI_PHY) += qsemi.o obj-$(CONFIG_SMSC_PHY) += smsc.o obj-$(CONFIG_TERANETICS_PHY) += teranetics.o obj-$(CONFIG_VITESSE_PHY) += vitesse.o +obj-$(CONFIG_BCM_NET_PHYLIB) += bcm-phy-lib.o obj-$(CONFIG_BROADCOM_PHY) += broadcom.o obj-$(CONFIG_BCM63XX_PHY) += bcm63xx.o obj-$(CONFIG_BCM7XXX_PHY) += bcm7xxx.o diff --git a/drivers/net/phy/bcm-phy-lib.c b/drivers/net/phy/bcm-phy-lib.c new file mode 100644 index 0000000..dd79ea6 --- /dev/null +++ b/drivers/net/phy/bcm-phy-lib.c @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2015 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "bcm-phy-lib.h" +#include +#include +#include +#include + +#define MII_BCM_CHANNEL_WIDTH 0x2000 +#define BCM_CL45VEN_EEE_ADV 0x3c + +int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val) +{ + int rc; + + rc = phy_write(phydev, MII_BCM54XX_EXP_SEL, reg); + if (rc < 0) + return rc; + + return phy_write(phydev, MII_BCM54XX_EXP_DATA, val); +} +EXPORT_SYMBOL_GPL(bcm_phy_write_exp); + +int bcm_phy_read_exp(struct phy_device *phydev, u16 reg) +{ + int val; + + val = phy_write(phydev, MII_BCM54XX_EXP_SEL, reg); + if (val < 0) + return val; + + val = phy_read(phydev, MII_BCM54XX_EXP_DATA); + + /* Restore default value. It's O.K. if this write fails. */ + phy_write(phydev, MII_BCM54XX_EXP_SEL, 0); + + return val; +} +EXPORT_SYMBOL_GPL(bcm_phy_read_exp); + +int bcm_phy_write_misc(struct phy_device *phydev, + u16 reg, u16 chl, u16 val) +{ + int rc; + int tmp; + + rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, + MII_BCM54XX_AUXCTL_SHDWSEL_MISC); + if (rc < 0) + return rc; + + tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL); + tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA; + rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp); + if (rc < 0) + return rc; + + tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg; + rc = bcm_phy_write_exp(phydev, tmp, val); + + return rc; +} +EXPORT_SYMBOL_GPL(bcm_phy_write_misc); + +int bcm_phy_read_misc(struct phy_device *phydev, + u16 reg, u16 chl) +{ + int rc; + int tmp; + + rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, + MII_BCM54XX_AUXCTL_SHDWSEL_MISC); + if (rc < 0) + return rc; + + tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL); + tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA; + rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp); + if (rc < 0) + return rc; + + tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg; + rc = bcm_phy_read_exp(phydev, tmp); + + return rc; +} +EXPORT_SYMBOL_GPL(bcm_phy_read_misc); + +int bcm_phy_ack_intr(struct phy_device *phydev) +{ + int reg; + + /* Clear pending interrupts. */ + reg = phy_read(phydev, MII_BCM54XX_ISR); + if (reg < 0) + return reg; + + return 0; +} +EXPORT_SYMBOL_GPL(bcm_phy_ack_intr); + +int bcm_phy_config_intr(struct phy_device *phydev) +{ + int reg; + + reg = phy_read(phydev, MII_BCM54XX_ECR); + if (reg < 0) + return reg; + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) + reg &= ~MII_BCM54XX_ECR_IM; + else + reg |= MII_BCM54XX_ECR_IM; + + return phy_write(phydev, MII_BCM54XX_ECR, reg); +} +EXPORT_SYMBOL_GPL(bcm_phy_config_intr); + +int bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow) +{ + phy_write(phydev, MII_BCM54XX_SHD, MII_BCM54XX_SHD_VAL(shadow)); + return MII_BCM54XX_SHD_DATA(phy_read(phydev, MII_BCM54XX_SHD)); +} +EXPORT_SYMBOL_GPL(bcm_phy_read_shadow); + +int bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow, + u16 val) +{ + return phy_write(phydev, MII_BCM54XX_SHD, + MII_BCM54XX_SHD_WRITE | + MII_BCM54XX_SHD_VAL(shadow) | + MII_BCM54XX_SHD_DATA(val)); +} +EXPORT_SYMBOL_GPL(bcm_phy_write_shadow); + +int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down) +{ + int val; + + if (dll_pwr_down) { + val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3); + if (val < 0) + return val; + + val |= BCM54XX_SHD_SCR3_DLLAPD_DIS; + bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val); + } + + val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD); + if (val < 0) + return val; + + /* Clear APD bits */ + val &= BCM_APD_CLR_MASK; + + if (phydev->autoneg == AUTONEG_ENABLE) + val |= BCM54XX_SHD_APD_EN; + else + val |= BCM_NO_ANEG_APD_EN; + + /* Enable energy detect single link pulse for easy wakeup */ + val |= BCM_APD_SINGLELP_EN; + + /* Enable Auto Power-Down (APD) for the PHY */ + return bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val); +} +EXPORT_SYMBOL_GPL(bcm_phy_enable_apd); + +int bcm_phy_enable_eee(struct phy_device *phydev) +{ + int val; + + /* Enable EEE at PHY level */ + val = phy_read_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL, + MDIO_MMD_AN, phydev->addr); + if (val < 0) + return val; + + val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X; + + phy_write_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL, + MDIO_MMD_AN, phydev->addr, (u32)val); + + /* Advertise EEE */ + val = phy_read_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV, + MDIO_MMD_AN, phydev->addr); + if (val < 0) + return val; + + val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T); + + phy_write_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV, + MDIO_MMD_AN, phydev->addr, (u32)val); + + return 0; +} +EXPORT_SYMBOL_GPL(bcm_phy_enable_eee); diff --git a/drivers/net/phy/bcm-phy-lib.h b/drivers/net/phy/bcm-phy-lib.h new file mode 100644 index 0000000..b2091c8 --- /dev/null +++ b/drivers/net/phy/bcm-phy-lib.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2015 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _LINUX_BCM_PHY_LIB_H +#define _LINUX_BCM_PHY_LIB_H + +#include + +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 bcm_phy_write_misc(struct phy_device *phydev, + u16 reg, u16 chl, u16 value); +int bcm_phy_read_misc(struct phy_device *phydev, + u16 reg, u16 chl); + +int bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow, + u16 val); +int bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow); + +int bcm_phy_ack_intr(struct phy_device *phydev); +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); +#endif /* _LINUX_BCM_PHY_LIB_H */ diff --git a/drivers/net/phy/bcm63xx.c b/drivers/net/phy/bcm63xx.c index 830ec31..86b2805 100644 --- a/drivers/net/phy/bcm63xx.c +++ b/drivers/net/phy/bcm63xx.c @@ -6,6 +6,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ +#include "bcm-phy-lib.h" #include #include @@ -42,35 +43,6 @@ static int bcm63xx_config_init(struct phy_device *phydev) return phy_write(phydev, MII_BCM63XX_IR, reg); } -static int bcm63xx_ack_interrupt(struct phy_device *phydev) -{ - int reg; - - /* Clear pending interrupts. */ - reg = phy_read(phydev, MII_BCM63XX_IR); - if (reg < 0) - return reg; - - return 0; -} - -static int bcm63xx_config_intr(struct phy_device *phydev) -{ - int reg, err; - - reg = phy_read(phydev, MII_BCM63XX_IR); - if (reg < 0) - return reg; - - if (phydev->interrupts == PHY_INTERRUPT_ENABLED) - reg &= ~MII_BCM63XX_IR_GMASK; - else - reg |= MII_BCM63XX_IR_GMASK; - - err = phy_write(phydev, MII_BCM63XX_IR, reg); - return err; -} - static struct phy_driver bcm63xx_driver[] = { { .phy_id = 0x00406000, @@ -82,8 +54,8 @@ static struct phy_driver bcm63xx_driver[] = { .config_init = bcm63xx_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, - .ack_interrupt = bcm63xx_ack_interrupt, - .config_intr = bcm63xx_config_intr, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, .driver = { .owner = THIS_MODULE }, }, { /* same phy as above, with just a different OUI */ @@ -95,8 +67,8 @@ static struct phy_driver bcm63xx_driver[] = { .config_init = bcm63xx_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, - .ack_interrupt = bcm63xx_ack_interrupt, - .config_intr = bcm63xx_config_intr, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, .driver = { .owner = THIS_MODULE }, } }; diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c index 6b701b3..efa31a6 100644 --- a/drivers/net/phy/bcm7xxx.c +++ b/drivers/net/phy/bcm7xxx.c @@ -12,12 +12,12 @@ #include #include #include +#include "bcm-phy-lib.h" #include #include #include /* Broadcom BCM7xxx internal PHY registers */ -#define MII_BCM7XXX_CHANNEL_WIDTH 0x2000 /* 40nm only register definitions */ #define MII_BCM7XXX_100TX_AUX_CTL 0x10 @@ -48,37 +48,13 @@ #define CORE_EXPB0 0xb0 -static void phy_write_exp(struct phy_device *phydev, - u16 reg, u16 value) -{ - phy_write(phydev, MII_BCM54XX_EXP_SEL, MII_BCM54XX_EXP_SEL_ER | reg); - phy_write(phydev, MII_BCM54XX_EXP_DATA, value); -} - -static void phy_write_misc(struct phy_device *phydev, - u16 reg, u16 chl, u16 value) -{ - int tmp; - - phy_write(phydev, MII_BCM54XX_AUX_CTL, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); - - tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL); - tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA; - phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp); - - tmp = (chl * MII_BCM7XXX_CHANNEL_WIDTH) | reg; - phy_write(phydev, MII_BCM54XX_EXP_SEL, tmp); - - phy_write(phydev, MII_BCM54XX_EXP_DATA, value); -} - static void r_rc_cal_reset(struct phy_device *phydev) { /* Reset R_CAL/RC_CAL Engine */ - phy_write_exp(phydev, 0x00b0, 0x0010); + bcm_phy_write_exp(phydev, 0x00b0, 0x0010); /* Disable Reset R_AL/RC_CAL Engine */ - phy_write_exp(phydev, 0x00b0, 0x0000); + bcm_phy_write_exp(phydev, 0x00b0, 0x0000); } static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev) @@ -86,18 +62,18 @@ static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev) /* Increase VCO range to prevent unlocking problem of PLL at low * temp */ - phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048); + bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048); /* Change Ki to 011 */ - phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b); + bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b); /* Disable loading of TVCO buffer to bandgap, set bandgap trim * to 111 */ - phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20); + bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20); /* Adjust bias current trim by -3 */ - phy_write_misc(phydev, DSP_TAP10, 0x690b); + bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b); /* Switch to CORE_BASE1E */ phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0xd); @@ -105,19 +81,19 @@ static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev) r_rc_cal_reset(phydev); /* write AFE_RXCONFIG_0 */ - phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19); + bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19); /* write AFE_RXCONFIG_1 */ - phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f); + bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f); /* write AFE_RX_LP_COUNTER */ - phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); + bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); /* write AFE_HPF_TRIM_OTHERS */ - phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b); + bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b); /* write AFTE_TX_CONFIG */ - phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800); + bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800); return 0; } @@ -125,36 +101,36 @@ static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev) static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev) { /* AFE_RXCONFIG_0 */ - phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb15); + bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb15); /* AFE_RXCONFIG_1 */ - phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f); + bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f); /* AFE_RXCONFIG_2, set rCal offset for HT=0 code and LT=-2 code */ - phy_write_misc(phydev, AFE_RXCONFIG_2, 0x2003); + bcm_phy_write_misc(phydev, AFE_RXCONFIG_2, 0x2003); /* AFE_RX_LP_COUNTER, set RX bandwidth to maximum */ - phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); + bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); /* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */ - phy_write_misc(phydev, AFE_TX_CONFIG, 0x431); + bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x431); /* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */ - phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da); + bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da); /* AFE_VDAC_OTHERS_0, set 1000BT Cidac=010 for all ports */ - phy_write_misc(phydev, AFE_VDAC_OTHERS_0, 0xa020); + bcm_phy_write_misc(phydev, AFE_VDAC_OTHERS_0, 0xa020); /* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal * offset for HT=0 code */ - phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3); + bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3); /* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */ phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0x0010); /* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */ - phy_write_misc(phydev, DSP_TAP10, 0x011b); + bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b); /* Reset R_CAL/RC_CAL engine */ r_rc_cal_reset(phydev); @@ -165,24 +141,24 @@ static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev) static int bcm7xxx_28nm_e0_plus_afe_config_init(struct phy_device *phydev) { /* AFE_RXCONFIG_1, provide more margin for INL/DNL measurement */ - phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f); + bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f); /* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */ - phy_write_misc(phydev, AFE_TX_CONFIG, 0x431); + bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x431); /* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */ - phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da); + bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da); /* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal * offset for HT=0 code */ - phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3); + bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3); /* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */ phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0x0010); /* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */ - phy_write_misc(phydev, DSP_TAP10, 0x011b); + bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b); /* Reset R_CAL/RC_CAL engine */ r_rc_cal_reset(phydev); @@ -190,53 +166,6 @@ static int bcm7xxx_28nm_e0_plus_afe_config_init(struct phy_device *phydev) return 0; } -static int bcm7xxx_apd_enable(struct phy_device *phydev) -{ - int val; - - /* Enable powering down of the DLL during auto-power down */ - val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_SCR3); - if (val < 0) - return val; - - val |= BCM54XX_SHD_SCR3_DLLAPD_DIS; - bcm54xx_shadow_write(phydev, BCM54XX_SHD_SCR3, val); - - /* Enable auto-power down */ - val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_APD); - if (val < 0) - return val; - - val |= BCM54XX_SHD_APD_EN; - return bcm54xx_shadow_write(phydev, BCM54XX_SHD_APD, val); -} - -static int bcm7xxx_eee_enable(struct phy_device *phydev) -{ - int val; - - val = phy_read_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL, - MDIO_MMD_AN, phydev->addr); - if (val < 0) - return val; - - /* Enable general EEE feature at the PHY level */ - val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X; - - phy_write_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL, - MDIO_MMD_AN, phydev->addr, val); - - /* Advertise supported modes */ - val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV, - MDIO_MMD_AN, phydev->addr); - - val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T); - phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV, - MDIO_MMD_AN, phydev->addr, val); - - return 0; -} - static int bcm7xxx_28nm_config_init(struct phy_device *phydev) { u8 rev = PHY_BRCM_7XXX_REV(phydev->dev_flags); @@ -273,11 +202,11 @@ static int bcm7xxx_28nm_config_init(struct phy_device *phydev) if (ret) return ret; - ret = bcm7xxx_eee_enable(phydev); + ret = bcm_phy_enable_eee(phydev); if (ret) return ret; - return bcm7xxx_apd_enable(phydev); + return bcm_phy_enable_apd(phydev, true); } static int bcm7xxx_28nm_resume(struct phy_device *phydev) diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index 9c71295..07a6119 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -14,6 +14,7 @@ * 2 of the License, or (at your option) any later version. */ +#include "bcm-phy-lib.h" #include #include #include @@ -29,39 +30,6 @@ MODULE_DESCRIPTION("Broadcom PHY driver"); MODULE_AUTHOR("Maciej W. Rozycki"); MODULE_LICENSE("GPL"); -/* Indirect register access functions for the Expansion Registers */ -static int bcm54xx_exp_read(struct phy_device *phydev, u16 regnum) -{ - int val; - - val = phy_write(phydev, MII_BCM54XX_EXP_SEL, regnum); - if (val < 0) - return val; - - val = phy_read(phydev, MII_BCM54XX_EXP_DATA); - - /* Restore default value. It's O.K. if this write fails. */ - phy_write(phydev, MII_BCM54XX_EXP_SEL, 0); - - return val; -} - -static int bcm54xx_exp_write(struct phy_device *phydev, u16 regnum, u16 val) -{ - int ret; - - ret = phy_write(phydev, MII_BCM54XX_EXP_SEL, regnum); - if (ret < 0) - return ret; - - ret = phy_write(phydev, MII_BCM54XX_EXP_DATA, val); - - /* Restore default value. It's O.K. if this write fails. */ - phy_write(phydev, MII_BCM54XX_EXP_SEL, 0); - - return ret; -} - static int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val) { return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val); @@ -72,28 +40,28 @@ static int bcm50610_a0_workaround(struct phy_device *phydev) { int err; - err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_AADJ1CH0, + err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH0, MII_BCM54XX_EXP_AADJ1CH0_SWP_ABCD_OEN | MII_BCM54XX_EXP_AADJ1CH0_SWSEL_THPF); if (err < 0) return err; - err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_AADJ1CH3, - MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ); + err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH3, + MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ); if (err < 0) return err; - err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP75, + err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75, MII_BCM54XX_EXP_EXP75_VDACCTRL); if (err < 0) return err; - err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP96, + err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP96, MII_BCM54XX_EXP_EXP96_MYST); if (err < 0) return err; - err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP97, + err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP97, MII_BCM54XX_EXP_EXP97_MYST); return err; @@ -114,7 +82,7 @@ static int bcm54xx_phydsp_config(struct phy_device *phydev) if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 || BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) { /* Clear bit 9 to fix a phy interop issue. */ - err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP08, + err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08, MII_BCM54XX_EXP_EXP08_RJCT_2MHZ); if (err < 0) goto error; @@ -129,12 +97,12 @@ static int bcm54xx_phydsp_config(struct phy_device *phydev) if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM57780) { int val; - val = bcm54xx_exp_read(phydev, MII_BCM54XX_EXP_EXP75); + val = bcm_phy_read_exp(phydev, MII_BCM54XX_EXP_EXP75); if (val < 0) goto error; val |= MII_BCM54XX_EXP_EXP75_CM_OSC; - err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP75, val); + err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75, val); } error: @@ -159,7 +127,7 @@ static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev) BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610M) return; - val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_SCR3); + val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3); if (val < 0) return; @@ -190,9 +158,9 @@ static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev) val |= BCM54XX_SHD_SCR3_TRDDAPD; if (orig != val) - bcm54xx_shadow_write(phydev, BCM54XX_SHD_SCR3, val); + bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val); - val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_APD); + val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD); if (val < 0) return; @@ -204,7 +172,7 @@ static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev) val &= ~BCM54XX_SHD_APD_EN; if (orig != val) - bcm54xx_shadow_write(phydev, BCM54XX_SHD_APD, val); + bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val); } static int bcm54xx_config_init(struct phy_device *phydev) @@ -232,7 +200,7 @@ static int bcm54xx_config_init(struct phy_device *phydev) if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 || BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) && (phydev->dev_flags & PHY_BRCM_CLEAR_RGMII_MODE)) - bcm54xx_shadow_write(phydev, BCM54XX_SHD_RGMII_MODE, 0); + bcm_phy_write_shadow(phydev, BCM54XX_SHD_RGMII_MODE, 0); if ((phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) || (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY) || @@ -254,8 +222,8 @@ static int bcm5482_config_init(struct phy_device *phydev) /* * Enable secondary SerDes and its use as an LED source */ - reg = bcm54xx_shadow_read(phydev, BCM5482_SHD_SSD); - bcm54xx_shadow_write(phydev, BCM5482_SHD_SSD, + reg = bcm_phy_read_shadow(phydev, BCM5482_SHD_SSD); + bcm_phy_write_shadow(phydev, BCM5482_SHD_SSD, reg | BCM5482_SHD_SSD_LEDM | BCM5482_SHD_SSD_EN); @@ -264,10 +232,10 @@ static int bcm5482_config_init(struct phy_device *phydev) * Enable SGMII slave mode and auto-detection */ reg = BCM5482_SSD_SGMII_SLAVE | MII_BCM54XX_EXP_SEL_SSD; - err = bcm54xx_exp_read(phydev, reg); + err = bcm_phy_read_exp(phydev, reg); if (err < 0) return err; - err = bcm54xx_exp_write(phydev, reg, err | + err = bcm_phy_write_exp(phydev, reg, err | BCM5482_SSD_SGMII_SLAVE_EN | BCM5482_SSD_SGMII_SLAVE_AD); if (err < 0) @@ -277,10 +245,10 @@ static int bcm5482_config_init(struct phy_device *phydev) * Disable secondary SerDes powerdown */ reg = BCM5482_SSD_1000BX_CTL | MII_BCM54XX_EXP_SEL_SSD; - err = bcm54xx_exp_read(phydev, reg); + err = bcm_phy_read_exp(phydev, reg); if (err < 0) return err; - err = bcm54xx_exp_write(phydev, reg, + err = bcm_phy_write_exp(phydev, reg, err & ~BCM5482_SSD_1000BX_CTL_PWRDOWN); if (err < 0) return err; @@ -288,15 +256,15 @@ static int bcm5482_config_init(struct phy_device *phydev) /* * Select 1000BASE-X register set (primary SerDes) */ - reg = bcm54xx_shadow_read(phydev, BCM5482_SHD_MODE); - bcm54xx_shadow_write(phydev, BCM5482_SHD_MODE, + reg = bcm_phy_read_shadow(phydev, BCM5482_SHD_MODE); + bcm_phy_write_shadow(phydev, BCM5482_SHD_MODE, reg | BCM5482_SHD_MODE_1000BX); /* * LED1=ACTIVITYLED, LED3=LINKSPD[2] * (Use LED1 as secondary SerDes ACTIVITY LED) */ - bcm54xx_shadow_write(phydev, BCM5482_SHD_LEDS1, + bcm_phy_write_shadow(phydev, BCM5482_SHD_LEDS1, BCM5482_SHD_LEDS1_LED1(BCM_LED_SRC_ACTIVITYLED) | BCM5482_SHD_LEDS1_LED3(BCM_LED_SRC_LINKSPD2)); @@ -334,35 +302,6 @@ static int bcm5482_read_status(struct phy_device *phydev) return err; } -static int bcm54xx_ack_interrupt(struct phy_device *phydev) -{ - int reg; - - /* Clear pending interrupts. */ - reg = phy_read(phydev, MII_BCM54XX_ISR); - if (reg < 0) - return reg; - - return 0; -} - -static int bcm54xx_config_intr(struct phy_device *phydev) -{ - int reg, err; - - reg = phy_read(phydev, MII_BCM54XX_ECR); - if (reg < 0) - return reg; - - if (phydev->interrupts == PHY_INTERRUPT_ENABLED) - reg &= ~MII_BCM54XX_ECR_IM; - else - reg |= MII_BCM54XX_ECR_IM; - - err = phy_write(phydev, MII_BCM54XX_ECR, reg); - return err; -} - static int bcm5481_config_aneg(struct phy_device *phydev) { int ret; @@ -519,8 +458,8 @@ static struct phy_driver broadcom_drivers[] = { .config_init = bcm54xx_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, - .ack_interrupt = bcm54xx_ack_interrupt, - .config_intr = bcm54xx_config_intr, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM5421, @@ -532,8 +471,8 @@ static struct phy_driver broadcom_drivers[] = { .config_init = bcm54xx_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, - .ack_interrupt = bcm54xx_ack_interrupt, - .config_intr = bcm54xx_config_intr, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM5461, @@ -545,8 +484,8 @@ static struct phy_driver broadcom_drivers[] = { .config_init = bcm54xx_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, - .ack_interrupt = bcm54xx_ack_interrupt, - .config_intr = bcm54xx_config_intr, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM54616S, @@ -558,8 +497,8 @@ static struct phy_driver broadcom_drivers[] = { .config_init = bcm54xx_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, - .ack_interrupt = bcm54xx_ack_interrupt, - .config_intr = bcm54xx_config_intr, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM5464, @@ -571,8 +510,8 @@ static struct phy_driver broadcom_drivers[] = { .config_init = bcm54xx_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, - .ack_interrupt = bcm54xx_ack_interrupt, - .config_intr = bcm54xx_config_intr, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM5481, @@ -584,8 +523,8 @@ static struct phy_driver broadcom_drivers[] = { .config_init = bcm54xx_config_init, .config_aneg = bcm5481_config_aneg, .read_status = genphy_read_status, - .ack_interrupt = bcm54xx_ack_interrupt, - .config_intr = bcm54xx_config_intr, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM5482, @@ -597,8 +536,8 @@ static struct phy_driver broadcom_drivers[] = { .config_init = bcm5482_config_init, .config_aneg = genphy_config_aneg, .read_status = bcm5482_read_status, - .ack_interrupt = bcm54xx_ack_interrupt, - .config_intr = bcm54xx_config_intr, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM50610, @@ -610,8 +549,8 @@ static struct phy_driver broadcom_drivers[] = { .config_init = bcm54xx_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, - .ack_interrupt = bcm54xx_ack_interrupt, - .config_intr = bcm54xx_config_intr, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM50610M, @@ -623,8 +562,8 @@ static struct phy_driver broadcom_drivers[] = { .config_init = bcm54xx_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, - .ack_interrupt = bcm54xx_ack_interrupt, - .config_intr = bcm54xx_config_intr, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM57780, @@ -636,8 +575,8 @@ static struct phy_driver broadcom_drivers[] = { .config_init = bcm54xx_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, - .ack_interrupt = bcm54xx_ack_interrupt, - .config_intr = bcm54xx_config_intr, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCMAC131, diff --git a/include/linux/brcmphy.h b/include/linux/brcmphy.h index 697ca77..6a53ab9 100644 --- a/include/linux/brcmphy.h +++ b/include/linux/brcmphy.h @@ -138,7 +138,10 @@ /* 01010: Auto Power-Down */ #define BCM54XX_SHD_APD 0x0a +#define BCM_APD_CLR_MASK 0xFE9F /* clear bits 5, 6 & 8 */ #define BCM54XX_SHD_APD_EN 0x0020 +#define BCM_NO_ANEG_APD_EN 0x0060 /* bits 5 & 6 */ +#define BCM_APD_SINGLELP_EN 0x0100 /* Bit 8 */ #define BCM5482_SHD_LEDS1 0x0d /* 01101: LED Selector 1 */ /* LED3 / ~LINKSPD[2] selector */ @@ -209,25 +212,6 @@ #define MII_BRCM_FET_SHDW_AUXSTAT2 0x1b /* Auxiliary status 2 */ #define MII_BRCM_FET_SHDW_AS2_APDE 0x0020 /* Auto power down enable */ -/* - * Indirect register access functions for the 1000BASE-T/100BASE-TX/10BASE-T - * 0x1c shadow registers. - */ -static inline int bcm54xx_shadow_read(struct phy_device *phydev, u16 shadow) -{ - phy_write(phydev, MII_BCM54XX_SHD, MII_BCM54XX_SHD_VAL(shadow)); - return MII_BCM54XX_SHD_DATA(phy_read(phydev, MII_BCM54XX_SHD)); -} - -static inline int bcm54xx_shadow_write(struct phy_device *phydev, u16 shadow, - u16 val) -{ - return phy_write(phydev, MII_BCM54XX_SHD, - MII_BCM54XX_SHD_WRITE | - MII_BCM54XX_SHD_VAL(shadow) | - MII_BCM54XX_SHD_DATA(val)); -} - #define BRCM_CL45VEN_EEE_CONTROL 0x803d #define LPI_FEATURE_EN 0x8000 #define LPI_FEATURE_EN_DIG1000X 0x4000 -- cgit v0.10.2 From 8e185d6997bb67068f0ca8f062a50caa2608cf1b Mon Sep 17 00:00:00 2001 From: Arun Parameswaran Date: Tue, 6 Oct 2015 12:25:49 -0700 Subject: net: phy: Broadcom Cygnus internal Etherent PHY driver Add support for the Broadcom Cygnus SoCs internal PHY's. The PHYs are 1000M/100M/10M capable with support for 'EEE' and 'APD' (Auto Power Down). This driver supports the following Broadcom Cygnus SoCs: - BCM583XX (BCM58300, BCM58302, BCM58303, BCM58305) - BCM113XX (BCM11300, BCM11320, BCM11350, BCM11360) The PHY's on these SoC's require some workarounds for stable operation, both during configuration time and during suspend/resume. This driver handles the application of the workarounds. Signed-off-by: Arun Parameswaran Signed-off-by: David S. Miller diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 606fdc9..9d097ae 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -79,6 +79,19 @@ config BROADCOM_PHY Currently supports the BCM5411, BCM5421, BCM5461, BCM54616S, BCM5464, BCM5481 and BCM5482 PHYs. +config BCM_CYGNUS_PHY + tristate "Drivers for Broadcom Cygnus SoC internal PHY" + depends on ARCH_BCM_CYGNUS || COMPILE_TEST + depends on MDIO_BCM_IPROC + select BCM_NET_PHYLIB + ---help--- + This PHY driver is for the 1G internal PHYs of the Broadcom + Cygnus Family SoC. + + Currently supports internal PHY's used in the BCM11300, + BCM11320, BCM11350, BCM11360, BCM58300, BCM58302, + BCM58303 & BCM58305 Broadcom Cygnus SoCs. + config BCM63XX_PHY tristate "Drivers for Broadcom 63xx SOCs internal PHY" depends on BCM63XX diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 6932475..7655d47 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_BROADCOM_PHY) += broadcom.o obj-$(CONFIG_BCM63XX_PHY) += bcm63xx.o obj-$(CONFIG_BCM7XXX_PHY) += bcm7xxx.o obj-$(CONFIG_BCM87XX_PHY) += bcm87xx.o +obj-$(CONFIG_BCM_CYGNUS_PHY) += bcm-cygnus.o obj-$(CONFIG_ICPLUS_PHY) += icplus.o obj-$(CONFIG_REALTEK_PHY) += realtek.o obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c.o diff --git a/drivers/net/phy/bcm-cygnus.c b/drivers/net/phy/bcm-cygnus.c new file mode 100644 index 0000000..49bbc68 --- /dev/null +++ b/drivers/net/phy/bcm-cygnus.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2015 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* Broadcom Cygnus SoC internal transceivers support. */ +#include "bcm-phy-lib.h" +#include +#include +#include +#include + +/* Broadcom Cygnus Phy specific registers */ +#define MII_BCM_CYGNUS_AFE_VDAC_ICTRL_0 0x91E5 /* VDAL Control register */ + +static int bcm_cygnus_afe_config(struct phy_device *phydev) +{ + int rc; + + /* ensure smdspclk is enabled */ + rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, 0x0c30); + if (rc < 0) + return rc; + + /* AFE_VDAC_ICTRL_0 bit 7:4 Iq=1100 for 1g 10bt, normal modes */ + rc = bcm_phy_write_misc(phydev, 0x39, 0x01, 0xA7C8); + if (rc < 0) + return rc; + + /* AFE_HPF_TRIM_OTHERS bit11=1, short cascode enable for all modes*/ + rc = bcm_phy_write_misc(phydev, 0x3A, 0x00, 0x0803); + if (rc < 0) + return rc; + + /* AFE_TX_CONFIG_1 bit 7:4 Iq=1100 for test modes */ + rc = bcm_phy_write_misc(phydev, 0x3A, 0x01, 0xA740); + if (rc < 0) + return rc; + + /* AFE TEMPSEN_OTHERS rcal_HT, rcal_LT 10000 */ + rc = bcm_phy_write_misc(phydev, 0x3A, 0x03, 0x8400); + if (rc < 0) + return rc; + + /* AFE_FUTURE_RSV bit 2:0 rccal <2:0>=100 */ + rc = bcm_phy_write_misc(phydev, 0x3B, 0x00, 0x0004); + if (rc < 0) + return rc; + + /* Adjust bias current trim to overcome digital offSet */ + rc = phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x02); + if (rc < 0) + return rc; + + /* make rcal=100, since rdb default is 000 */ + rc = bcm_phy_write_exp(phydev, MII_BRCM_CORE_EXPB1, 0x10); + if (rc < 0) + return rc; + + /* CORE_EXPB0, Reset R_CAL/RC_CAL Engine */ + rc = bcm_phy_write_exp(phydev, MII_BRCM_CORE_EXPB0, 0x10); + if (rc < 0) + return rc; + + /* CORE_EXPB0, Disable Reset R_CAL/RC_CAL Engine */ + rc = bcm_phy_write_exp(phydev, MII_BRCM_CORE_EXPB0, 0x00); + + return 0; +} + +static int bcm_cygnus_config_init(struct phy_device *phydev) +{ + int reg, rc; + + reg = phy_read(phydev, MII_BCM54XX_ECR); + if (reg < 0) + return reg; + + /* Mask interrupts globally. */ + reg |= MII_BCM54XX_ECR_IM; + rc = phy_write(phydev, MII_BCM54XX_ECR, reg); + if (rc) + return rc; + + /* Unmask events of interest */ + reg = ~(MII_BCM54XX_INT_DUPLEX | + MII_BCM54XX_INT_SPEED | + MII_BCM54XX_INT_LINK); + rc = phy_write(phydev, MII_BCM54XX_IMR, reg); + if (rc) + return rc; + + /* Apply AFE settings for the PHY */ + rc = bcm_cygnus_afe_config(phydev); + if (rc) + return rc; + + /* Advertise EEE */ + rc = bcm_phy_enable_eee(phydev); + if (rc) + return rc; + + /* Enable APD */ + return bcm_phy_enable_apd(phydev, false); +} + +static int bcm_cygnus_resume(struct phy_device *phydev) +{ + int rc; + + genphy_resume(phydev); + + /* Re-initialize the PHY to apply AFE work-arounds and + * configurations when coming out of suspend. + */ + rc = bcm_cygnus_config_init(phydev); + if (rc) + return rc; + + /* restart auto negotiation with the new settings */ + return genphy_config_aneg(phydev); +} + +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, + .config_init = bcm_cygnus_config_init, + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, + .suspend = genphy_suspend, + .resume = bcm_cygnus_resume, +} }; + +static struct mdio_device_id __maybe_unused bcm_cygnus_phy_tbl[] = { + { PHY_ID_BCM_CYGNUS, 0xfffffff0, }, + { } +}; +MODULE_DEVICE_TABLE(mdio, bcm_cygnus_phy_tbl); + +module_phy_driver(bcm_cygnus_phy_driver); + +MODULE_DESCRIPTION("Broadcom Cygnus internal PHY driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Broadcom Corporation"); diff --git a/include/linux/brcmphy.h b/include/linux/brcmphy.h index 6a53ab9..59f4a73 100644 --- a/include/linux/brcmphy.h +++ b/include/linux/brcmphy.h @@ -30,6 +30,8 @@ #define PHY_ID_BCM7439_2 0xae025080 #define PHY_ID_BCM7445 0x600d8510 +#define PHY_ID_BCM_CYGNUS 0xae025200 + #define PHY_BCM_OUI_MASK 0xfffffc00 #define PHY_BCM_OUI_1 0x00206000 #define PHY_BCM_OUI_2 0x0143bc00 @@ -216,4 +218,9 @@ #define LPI_FEATURE_EN 0x8000 #define LPI_FEATURE_EN_DIG1000X 0x4000 +/* Core register definitions*/ +#define MII_BRCM_CORE_BASE1E 0x1E +#define MII_BRCM_CORE_EXPB0 0xB0 +#define MII_BRCM_CORE_EXPB1 0xB1 + #endif /* _LINUX_BRCMPHY_H */ -- cgit v0.10.2 From 9200c27a1cc7ac719e4c43411e5a170144ba0ac5 Mon Sep 17 00:00:00 2001 From: Arun Parameswaran Date: Tue, 6 Oct 2015 12:25:50 -0700 Subject: net: phy: bcm7xxx: Modified to use global core register defines Modified the bcm7xxx phy driver to remove local core register defines and use the common ones from "include/linux/brcmphy.h" Signed-off-by: Arun Parameswaran Signed-off-by: David S. Miller diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c index efa31a6..03d4809 100644 --- a/drivers/net/phy/bcm7xxx.c +++ b/drivers/net/phy/bcm7xxx.c @@ -25,7 +25,6 @@ #define MII_BCM7XXX_100TX_DISC 0x14 #define MII_BCM7XXX_AUX_MODE 0x1d #define MII_BCM7XX_64CLK_MDIO BIT(12) -#define MII_BCM7XXX_CORE_BASE1E 0x1e #define MII_BCM7XXX_TEST 0x1f #define MII_BCM7XXX_SHD_MODE_2 BIT(2) @@ -46,8 +45,6 @@ #define AFE_VDAC_OTHERS_0 MISC_ADDR(0x39, 3) #define AFE_HPF_TRIM_OTHERS MISC_ADDR(0x3a, 0) -#define CORE_EXPB0 0xb0 - static void r_rc_cal_reset(struct phy_device *phydev) { /* Reset R_CAL/RC_CAL Engine */ @@ -76,7 +73,7 @@ static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev) bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b); /* Switch to CORE_BASE1E */ - phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0xd); + phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd); r_rc_cal_reset(phydev); @@ -127,7 +124,7 @@ static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev) bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3); /* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */ - phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0x0010); + phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x0010); /* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */ bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b); @@ -155,7 +152,7 @@ static int bcm7xxx_28nm_e0_plus_afe_config_init(struct phy_device *phydev) bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3); /* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */ - phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0x0010); + phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x0010); /* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */ bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b); -- cgit v0.10.2 From d70e53262f5cfc0b88a211ef9cbd1ca5256f4a6b Mon Sep 17 00:00:00 2001 From: Jon Ringle Date: Tue, 6 Oct 2015 16:37:46 -0400 Subject: net: Microchip encx24j600 driver This ethernet driver supports the Micorchip enc424j600/626j600 Ethernet controller over a SPI bus interface. This driver makes use of the regmap API to optimize access to registers by caching registers where possible. Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/39935b.pdf Signed-off-by: Jon Ringle Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/microchip/Kconfig b/drivers/net/ethernet/microchip/Kconfig index 3fd8ca6..992b014 100644 --- a/drivers/net/ethernet/microchip/Kconfig +++ b/drivers/net/ethernet/microchip/Kconfig @@ -33,4 +33,13 @@ config ENC28J60_WRITEVERIFY Enable the verify after the buffer write useful for debugging purpose. If unsure, say N. +config ENCX24J600 + tristate "ENCX24J600 support" + depends on SPI + ---help--- + Support for the Microchip ENC424J600 ethernet chip. + + To compile this driver as a module, choose M here. The module will be + called enc424j600. + endif # NET_VENDOR_MICROCHIP diff --git a/drivers/net/ethernet/microchip/Makefile b/drivers/net/ethernet/microchip/Makefile index 573d429..ff78f62 100644 --- a/drivers/net/ethernet/microchip/Makefile +++ b/drivers/net/ethernet/microchip/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_ENC28J60) += enc28j60.o +obj-$(CONFIG_ENCX24J600) += encx24j600.o encx24j600-regmap.o diff --git a/drivers/net/ethernet/microchip/encx24j600-regmap.c b/drivers/net/ethernet/microchip/encx24j600-regmap.c new file mode 100644 index 0000000..f3bb905 --- /dev/null +++ b/drivers/net/ethernet/microchip/encx24j600-regmap.c @@ -0,0 +1,513 @@ +/** + * Register map access API - ENCX24J600 support + * + * Copyright 2015 Gridpoint + * + * Author: Jon Ringle + * + * 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 "encx24j600_hw.h" + +static inline bool is_bits_set(int value, int mask) +{ + return (value & mask) == mask; +} + +static int encx24j600_switch_bank(struct encx24j600_context *ctx, + int bank) +{ + int ret = 0; + + int bank_opcode = BANK_SELECT(bank); + ret = spi_write(ctx->spi, &bank_opcode, 1); + if (ret == 0) + ctx->bank = bank; + + return ret; +} + +static int encx24j600_cmdn(struct encx24j600_context *ctx, u8 opcode, + const void *buf, size_t len) +{ + struct spi_message m; + struct spi_transfer t[2] = { { .tx_buf = &opcode, .len = 1, }, + { .tx_buf = buf, .len = len }, }; + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + + return spi_sync(ctx->spi, &m); +} + +static void regmap_lock_mutex(void *context) +{ + struct encx24j600_context *ctx = context; + mutex_lock(&ctx->mutex); +} + +static void regmap_unlock_mutex(void *context) +{ + struct encx24j600_context *ctx = context; + mutex_unlock(&ctx->mutex); +} + +static int regmap_encx24j600_sfr_read(void *context, u8 reg, u8 *val, + size_t len) +{ + struct encx24j600_context *ctx = context; + u8 banked_reg = reg & ADDR_MASK; + u8 bank = ((reg & BANK_MASK) >> BANK_SHIFT); + u8 cmd = RCRU; + int ret = 0; + int i = 0; + u8 tx_buf[2]; + + if (reg < 0x80) { + cmd = RCRCODE | banked_reg; + if ((banked_reg < 0x16) && (ctx->bank != bank)) + ret = encx24j600_switch_bank(ctx, bank); + if (unlikely(ret)) + return ret; + } else { + /* Translate registers that are more effecient using + * 3-byte SPI commands + */ + switch (reg) { + case EGPRDPT: + cmd = RGPRDPT; break; + case EGPWRPT: + cmd = RGPWRPT; break; + case ERXRDPT: + cmd = RRXRDPT; break; + case ERXWRPT: + cmd = RRXWRPT; break; + case EUDARDPT: + cmd = RUDARDPT; break; + case EUDAWRPT: + cmd = RUDAWRPT; break; + case EGPDATA: + case ERXDATA: + case EUDADATA: + default: + return -EINVAL; + } + } + + tx_buf[i++] = cmd; + if (cmd == RCRU) + tx_buf[i++] = reg; + + ret = spi_write_then_read(ctx->spi, tx_buf, i, val, len); + + return ret; +} + +static int regmap_encx24j600_sfr_update(struct encx24j600_context *ctx, + u8 reg, u8 *val, size_t len, + u8 unbanked_cmd, u8 banked_code) +{ + u8 banked_reg = reg & ADDR_MASK; + u8 bank = ((reg & BANK_MASK) >> BANK_SHIFT); + u8 cmd = unbanked_cmd; + struct spi_message m; + struct spi_transfer t[3] = { { .tx_buf = &cmd, .len = sizeof(cmd), }, + { .tx_buf = ®, .len = sizeof(reg), }, + { .tx_buf = val, .len = len }, }; + + if (reg < 0x80) { + int ret = 0; + cmd = banked_code | banked_reg; + if ((banked_reg < 0x16) && (ctx->bank != bank)) + ret = encx24j600_switch_bank(ctx, bank); + if (unlikely(ret)) + return ret; + } else { + /* Translate registers that are more effecient using + * 3-byte SPI commands + */ + switch (reg) { + case EGPRDPT: + cmd = WGPRDPT; break; + case EGPWRPT: + cmd = WGPWRPT; break; + case ERXRDPT: + cmd = WRXRDPT; break; + case ERXWRPT: + cmd = WRXWRPT; break; + case EUDARDPT: + cmd = WUDARDPT; break; + case EUDAWRPT: + cmd = WUDAWRPT; break; + case EGPDATA: + case ERXDATA: + case EUDADATA: + default: + return -EINVAL; + } + } + + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + + if (cmd == unbanked_cmd) { + t[1].tx_buf = ® + spi_message_add_tail(&t[1], &m); + } + + spi_message_add_tail(&t[2], &m); + return spi_sync(ctx->spi, &m); +} + +static int regmap_encx24j600_sfr_write(void *context, u8 reg, u8 *val, + size_t len) +{ + struct encx24j600_context *ctx = context; + return regmap_encx24j600_sfr_update(ctx, reg, val, len, WCRU, WCRCODE); +} + +static int regmap_encx24j600_sfr_set_bits(struct encx24j600_context *ctx, + u8 reg, u8 val) +{ + return regmap_encx24j600_sfr_update(ctx, reg, &val, 1, BFSU, BFSCODE); +} + +static int regmap_encx24j600_sfr_clr_bits(struct encx24j600_context *ctx, + u8 reg, u8 val) +{ + return regmap_encx24j600_sfr_update(ctx, reg, &val, 1, BFCU, BFCCODE); +} + +static int regmap_encx24j600_reg_update_bits(void *context, unsigned int reg, + unsigned int mask, + unsigned int val) +{ + struct encx24j600_context *ctx = context; + + int ret = 0; + unsigned int set_mask = mask & val; + unsigned int clr_mask = mask & ~val; + + if ((reg >= 0x40 && reg < 0x6c) || reg >= 0x80) + return -EINVAL; + + if (set_mask & 0xff) + ret = regmap_encx24j600_sfr_set_bits(ctx, reg, set_mask); + + set_mask = (set_mask & 0xff00) >> 8; + + if ((set_mask & 0xff) && (ret == 0)) + ret = regmap_encx24j600_sfr_set_bits(ctx, reg + 1, set_mask); + + if ((clr_mask & 0xff) && (ret == 0)) + ret = regmap_encx24j600_sfr_clr_bits(ctx, reg, clr_mask); + + clr_mask = (clr_mask & 0xff00) >> 8; + + if ((clr_mask & 0xff) && (ret == 0)) + ret = regmap_encx24j600_sfr_clr_bits(ctx, reg + 1, clr_mask); + + return ret; +} + +int regmap_encx24j600_spi_write(void *context, u8 reg, const u8 *data, + size_t count) +{ + struct encx24j600_context *ctx = context; + + if (reg < 0xc0) + return encx24j600_cmdn(ctx, reg, data, count); + else + /* SPI 1-byte command. Ignore data */ + return spi_write(ctx->spi, ®, 1); +} +EXPORT_SYMBOL_GPL(regmap_encx24j600_spi_write); + +int regmap_encx24j600_spi_read(void *context, u8 reg, u8 *data, size_t count) +{ + struct encx24j600_context *ctx = context; + + if (reg == RBSEL && count > 1) + count = 1; + + return spi_write_then_read(ctx->spi, ®, sizeof(reg), data, count); +} +EXPORT_SYMBOL_GPL(regmap_encx24j600_spi_read); + +static int regmap_encx24j600_write(void *context, const void *data, + size_t len) +{ + u8 *dout = (u8 *)data; + u8 reg = dout[0]; + ++dout; + --len; + + if (reg > 0xa0) + return regmap_encx24j600_spi_write(context, reg, dout, len); + + if (len > 2) + return -EINVAL; + + return regmap_encx24j600_sfr_write(context, reg, dout, len); +} + +static int regmap_encx24j600_read(void *context, + const void *reg_buf, size_t reg_size, + void *val, size_t val_size) +{ + u8 reg = *(const u8 *)reg_buf; + + if (reg_size != 1) { + pr_err("%s: reg=%02x reg_size=%zu\n", __func__, reg, reg_size); + return -EINVAL; + } + + if (reg > 0xa0) + return regmap_encx24j600_spi_read(context, reg, val, val_size); + + if (val_size > 2) { + pr_err("%s: reg=%02x val_size=%zu\n", __func__, reg, val_size); + return -EINVAL; + } + + return regmap_encx24j600_sfr_read(context, reg, val, val_size); +} + +static bool encx24j600_regmap_readable(struct device *dev, unsigned int reg) +{ + if ((reg < 0x36) || + ((reg >= 0x40) && (reg < 0x4c)) || + ((reg >= 0x52) && (reg < 0x56)) || + ((reg >= 0x60) && (reg < 0x66)) || + ((reg >= 0x68) && (reg < 0x80)) || + ((reg >= 0x86) && (reg < 0x92)) || + (reg == 0xc8)) + return true; + else + return false; +} + +static bool encx24j600_regmap_writeable(struct device *dev, unsigned int reg) +{ + if ((reg < 0x12) || + ((reg >= 0x14) && (reg < 0x1a)) || + ((reg >= 0x1c) && (reg < 0x36)) || + ((reg >= 0x40) && (reg < 0x4c)) || + ((reg >= 0x52) && (reg < 0x56)) || + ((reg >= 0x60) && (reg < 0x68)) || + ((reg >= 0x6c) && (reg < 0x80)) || + ((reg >= 0x86) && (reg < 0x92)) || + ((reg >= 0xc0) && (reg < 0xc8)) || + ((reg >= 0xca) && (reg < 0xf0))) + return true; + else + return false; +} + +static bool encx24j600_regmap_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ERXHEAD: + case EDMACS: + case ETXSTAT: + case ETXWIRE: + case ECON1: /* Can be modified via single byte cmds */ + case ECON2: /* Can be modified via single byte cmds */ + case ESTAT: + case EIR: /* Can be modified via single byte cmds */ + case MIRD: + case MISTAT: + return true; + default: + break; + } + + return false; +} + +static bool encx24j600_regmap_precious(struct device *dev, unsigned int reg) +{ + /* single byte cmds are precious */ + if (((reg >= 0xc0) && (reg < 0xc8)) || + ((reg >= 0xca) && (reg < 0xf0))) + return true; + else + return false; +} + +static int regmap_encx24j600_phy_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct encx24j600_context *ctx = context; + int ret; + unsigned int mistat; + + reg = MIREGADR_VAL | (reg & PHREG_MASK); + ret = regmap_write(ctx->regmap, MIREGADR, reg); + if (unlikely(ret)) + goto err_out; + + ret = regmap_write(ctx->regmap, MICMD, MIIRD); + if (unlikely(ret)) + goto err_out; + + usleep_range(26, 100); + while ((ret = regmap_read(ctx->regmap, MISTAT, &mistat) != 0) && + (mistat & BUSY)) + cpu_relax(); + + if (unlikely(ret)) + goto err_out; + + ret = regmap_write(ctx->regmap, MICMD, 0); + if (unlikely(ret)) + goto err_out; + + ret = regmap_read(ctx->regmap, MIRD, val); + +err_out: + if (ret) + pr_err("%s: error %d reading reg %02x\n", __func__, ret, + reg & PHREG_MASK); + + return ret; +} + +static int regmap_encx24j600_phy_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct encx24j600_context *ctx = context; + int ret; + unsigned int mistat; + + reg = MIREGADR_VAL | (reg & PHREG_MASK); + ret = regmap_write(ctx->regmap, MIREGADR, reg); + if (unlikely(ret)) + goto err_out; + + ret = regmap_write(ctx->regmap, MIWR, val); + if (unlikely(ret)) + goto err_out; + + usleep_range(26, 100); + while ((ret = regmap_read(ctx->regmap, MISTAT, &mistat) != 0) && + (mistat & BUSY)) + cpu_relax(); + +err_out: + if (ret) + pr_err("%s: error %d writing reg %02x=%04x\n", __func__, ret, + reg & PHREG_MASK, val); + + return ret; +} + +static bool encx24j600_phymap_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PHCON1: + case PHSTAT1: + case PHANA: + case PHANLPA: + case PHANE: + case PHCON2: + case PHSTAT2: + case PHSTAT3: + return true; + default: + return false; + } +} + +static bool encx24j600_phymap_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PHCON1: + case PHCON2: + case PHANA: + return true; + case PHSTAT1: + case PHSTAT2: + case PHSTAT3: + case PHANLPA: + case PHANE: + default: + return false; + } +} + +static bool encx24j600_phymap_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PHSTAT1: + case PHSTAT2: + case PHSTAT3: + case PHANLPA: + case PHANE: + case PHCON2: + return true; + default: + return false; + } +} + +static struct regmap_config regcfg = { + .name = "reg", + .reg_bits = 8, + .val_bits = 16, + .max_register = 0xee, + .reg_stride = 2, + .cache_type = REGCACHE_RBTREE, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .readable_reg = encx24j600_regmap_readable, + .writeable_reg = encx24j600_regmap_writeable, + .volatile_reg = encx24j600_regmap_volatile, + .precious_reg = encx24j600_regmap_precious, + .lock = regmap_lock_mutex, + .unlock = regmap_unlock_mutex, +}; + +static struct regmap_bus regmap_encx24j600 = { + .write = regmap_encx24j600_write, + .read = regmap_encx24j600_read, + .reg_update_bits = regmap_encx24j600_reg_update_bits, +}; + +static struct regmap_config phycfg = { + .name = "phy", + .reg_bits = 8, + .val_bits = 16, + .max_register = 0x1f, + .cache_type = REGCACHE_RBTREE, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .readable_reg = encx24j600_phymap_readable, + .writeable_reg = encx24j600_phymap_writeable, + .volatile_reg = encx24j600_phymap_volatile, +}; +static struct regmap_bus phymap_encx24j600 = { + .reg_write = regmap_encx24j600_phy_reg_write, + .reg_read = regmap_encx24j600_phy_reg_read, +}; + +void devm_regmap_init_encx24j600(struct device *dev, + struct encx24j600_context *ctx) +{ + mutex_init(&ctx->mutex); + regcfg.lock_arg = ctx; + ctx->regmap = devm_regmap_init(dev, ®map_encx24j600, ctx, ®cfg); + ctx->phymap = devm_regmap_init(dev, &phymap_encx24j600, ctx, &phycfg); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_encx24j600); + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/microchip/encx24j600.c b/drivers/net/ethernet/microchip/encx24j600.c new file mode 100644 index 0000000..e1329d9 --- /dev/null +++ b/drivers/net/ethernet/microchip/encx24j600.c @@ -0,0 +1,1127 @@ +/** + * Microchip ENCX24J600 ethernet driver + * + * Copyright (C) 2015 Gridpoint + * Author: Jon Ringle + * + * 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 "encx24j600_hw.h" + +#define DRV_NAME "encx24j600" +#define DRV_VERSION "1.0" + +#define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK) +static int debug = -1; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); + +/* SRAM memory layout: + * + * 0x0000-0x05ff TX buffers 1.5KB (1*1536) reside in the GP area in SRAM + * 0x0600-0x5fff RX buffers 22.5KB (15*1536) reside in the RX area in SRAM + */ +#define ENC_TX_BUF_START 0x0000U +#define ENC_RX_BUF_START 0x0600U +#define ENC_RX_BUF_END 0x5fffU +#define ENC_SRAM_SIZE 0x6000U + +enum { + RXFILTER_NORMAL, + RXFILTER_MULTI, + RXFILTER_PROMISC +}; + +struct encx24j600_priv { + struct net_device *ndev; + struct mutex lock; /* device access lock */ + struct encx24j600_context ctx; + struct sk_buff *tx_skb; + struct task_struct *kworker_task; + struct kthread_worker kworker; + struct kthread_work tx_work; + struct kthread_work setrx_work; + u16 next_packet; + bool hw_enabled; + bool full_duplex; + bool autoneg; + u16 speed; + int rxfilter; + u32 msg_enable; +}; + +static void dump_packet(const char *msg, int len, const char *data) +{ + pr_debug(DRV_NAME ": %s - packet len:%d\n", msg, len); + print_hex_dump_bytes("pk data: ", DUMP_PREFIX_OFFSET, data, len); +} + +static void encx24j600_dump_rsv(struct encx24j600_priv *priv, const char *msg, + struct rsv *rsv) +{ + struct net_device *dev = priv->ndev; + + netdev_info(dev, "RX packet Len:%d\n", rsv->len); + netdev_dbg(dev, "%s - NextPk: 0x%04x\n", msg, + rsv->next_packet); + netdev_dbg(dev, "RxOK: %d, DribbleNibble: %d\n", + RSV_GETBIT(rsv->rxstat, RSV_RXOK), + RSV_GETBIT(rsv->rxstat, RSV_DRIBBLENIBBLE)); + netdev_dbg(dev, "CRCErr:%d, LenChkErr: %d, LenOutOfRange: %d\n", + RSV_GETBIT(rsv->rxstat, RSV_CRCERROR), + RSV_GETBIT(rsv->rxstat, RSV_LENCHECKERR), + RSV_GETBIT(rsv->rxstat, RSV_LENOUTOFRANGE)); + netdev_dbg(dev, "Multicast: %d, Broadcast: %d, LongDropEvent: %d, CarrierEvent: %d\n", + RSV_GETBIT(rsv->rxstat, RSV_RXMULTICAST), + RSV_GETBIT(rsv->rxstat, RSV_RXBROADCAST), + RSV_GETBIT(rsv->rxstat, RSV_RXLONGEVDROPEV), + RSV_GETBIT(rsv->rxstat, RSV_CARRIEREV)); + netdev_dbg(dev, "ControlFrame: %d, PauseFrame: %d, UnknownOp: %d, VLanTagFrame: %d\n", + RSV_GETBIT(rsv->rxstat, RSV_RXCONTROLFRAME), + RSV_GETBIT(rsv->rxstat, RSV_RXPAUSEFRAME), + RSV_GETBIT(rsv->rxstat, RSV_RXUNKNOWNOPCODE), + RSV_GETBIT(rsv->rxstat, RSV_RXTYPEVLAN)); +} + +static u16 encx24j600_read_reg(struct encx24j600_priv *priv, u8 reg) +{ + struct net_device *dev = priv->ndev; + unsigned int val = 0; + int ret = regmap_read(priv->ctx.regmap, reg, &val); + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d reading reg %02x\n", + __func__, ret, reg); + return val; +} + +static void encx24j600_write_reg(struct encx24j600_priv *priv, u8 reg, u16 val) +{ + struct net_device *dev = priv->ndev; + int ret = regmap_write(priv->ctx.regmap, reg, val); + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d writing reg %02x=%04x\n", + __func__, ret, reg, val); +} + +static void encx24j600_update_reg(struct encx24j600_priv *priv, u8 reg, + u16 mask, u16 val) +{ + struct net_device *dev = priv->ndev; + int ret = regmap_update_bits(priv->ctx.regmap, reg, mask, val); + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d updating reg %02x=%04x~%04x\n", + __func__, ret, reg, val, mask); +} + +static u16 encx24j600_read_phy(struct encx24j600_priv *priv, u8 reg) +{ + struct net_device *dev = priv->ndev; + unsigned int val = 0; + int ret = regmap_read(priv->ctx.phymap, reg, &val); + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d reading %02x\n", + __func__, ret, reg); + return val; +} + +static void encx24j600_write_phy(struct encx24j600_priv *priv, u8 reg, u16 val) +{ + struct net_device *dev = priv->ndev; + int ret = regmap_write(priv->ctx.phymap, reg, val); + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d writing reg %02x=%04x\n", + __func__, ret, reg, val); +} + +static void encx24j600_clr_bits(struct encx24j600_priv *priv, u8 reg, u16 mask) +{ + encx24j600_update_reg(priv, reg, mask, 0); +} + +static void encx24j600_set_bits(struct encx24j600_priv *priv, u8 reg, u16 mask) +{ + encx24j600_update_reg(priv, reg, mask, mask); +} + +static void encx24j600_cmd(struct encx24j600_priv *priv, u8 cmd) +{ + struct net_device *dev = priv->ndev; + int ret = regmap_write(priv->ctx.regmap, cmd, 0); + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d with cmd %02x\n", + __func__, ret, cmd); +} + +static int encx24j600_raw_read(struct encx24j600_priv *priv, u8 reg, u8 *data, + size_t count) +{ + int ret; + mutex_lock(&priv->ctx.mutex); + ret = regmap_encx24j600_spi_read(&priv->ctx, reg, data, count); + mutex_unlock(&priv->ctx.mutex); + + return ret; +} + +static int encx24j600_raw_write(struct encx24j600_priv *priv, u8 reg, + const u8 *data, size_t count) +{ + int ret; + mutex_lock(&priv->ctx.mutex); + ret = regmap_encx24j600_spi_write(&priv->ctx, reg, data, count); + mutex_unlock(&priv->ctx.mutex); + + return ret; +} + +static void encx24j600_update_phcon1(struct encx24j600_priv *priv) +{ + u16 phcon1 = encx24j600_read_phy(priv, PHCON1); + if (priv->autoneg == AUTONEG_ENABLE) { + phcon1 |= ANEN | RENEG; + } else { + phcon1 &= ~ANEN; + if (priv->speed == SPEED_100) + phcon1 |= SPD100; + else + phcon1 &= ~SPD100; + + if (priv->full_duplex) + phcon1 |= PFULDPX; + else + phcon1 &= ~PFULDPX; + } + encx24j600_write_phy(priv, PHCON1, phcon1); +} + +/* Waits for autonegotiation to complete. */ +static int encx24j600_wait_for_autoneg(struct encx24j600_priv *priv) +{ + struct net_device *dev = priv->ndev; + unsigned long timeout = jiffies + msecs_to_jiffies(2000); + u16 phstat1; + u16 estat; + int ret = 0; + + phstat1 = encx24j600_read_phy(priv, PHSTAT1); + while ((phstat1 & ANDONE) == 0) { + if (time_after(jiffies, timeout)) { + u16 phstat3; + + netif_notice(priv, drv, dev, "timeout waiting for autoneg done\n"); + + priv->autoneg = AUTONEG_DISABLE; + phstat3 = encx24j600_read_phy(priv, PHSTAT3); + priv->speed = (phstat3 & PHY3SPD100) + ? SPEED_100 : SPEED_10; + priv->full_duplex = (phstat3 & PHY3DPX) ? 1 : 0; + encx24j600_update_phcon1(priv); + netif_notice(priv, drv, dev, "Using parallel detection: %s/%s", + priv->speed == SPEED_100 ? "100" : "10", + priv->full_duplex ? "Full" : "Half"); + + return -ETIMEDOUT; + } + cpu_relax(); + phstat1 = encx24j600_read_phy(priv, PHSTAT1); + } + + estat = encx24j600_read_reg(priv, ESTAT); + if (estat & PHYDPX) { + encx24j600_set_bits(priv, MACON2, FULDPX); + encx24j600_write_reg(priv, MABBIPG, 0x15); + } else { + encx24j600_clr_bits(priv, MACON2, FULDPX); + encx24j600_write_reg(priv, MABBIPG, 0x12); + /* Max retransmittions attempt */ + encx24j600_write_reg(priv, MACLCON, 0x370f); + } + + return ret; +} + +/* Access the PHY to determine link status */ +static void encx24j600_check_link_status(struct encx24j600_priv *priv) +{ + struct net_device *dev = priv->ndev; + u16 estat; + + estat = encx24j600_read_reg(priv, ESTAT); + + if (estat & PHYLNK) { + if (priv->autoneg == AUTONEG_ENABLE) + encx24j600_wait_for_autoneg(priv); + + netif_carrier_on(dev); + netif_info(priv, ifup, dev, "link up\n"); + } else { + netif_info(priv, ifdown, dev, "link down\n"); + + /* Re-enable autoneg since we won't know what we might be + * connected to when the link is brought back up again. + */ + priv->autoneg = AUTONEG_ENABLE; + priv->full_duplex = true; + priv->speed = SPEED_100; + netif_carrier_off(dev); + } +} + +static void encx24j600_int_link_handler(struct encx24j600_priv *priv) +{ + struct net_device *dev = priv->ndev; + + netif_dbg(priv, intr, dev, "%s", __func__); + encx24j600_check_link_status(priv); + encx24j600_clr_bits(priv, EIR, LINKIF); +} + +static void encx24j600_tx_complete(struct encx24j600_priv *priv, bool err) +{ + struct net_device *dev = priv->ndev; + + if (!priv->tx_skb) { + BUG(); + return; + } + + mutex_lock(&priv->lock); + + if (err) + dev->stats.tx_errors++; + else + dev->stats.tx_packets++; + + dev->stats.tx_bytes += priv->tx_skb->len; + + encx24j600_clr_bits(priv, EIR, TXIF | TXABTIF); + + netif_dbg(priv, tx_done, dev, "TX Done%s\n", err ? ": Err" : ""); + + dev_kfree_skb(priv->tx_skb); + priv->tx_skb = NULL; + + netif_wake_queue(dev); + + mutex_unlock(&priv->lock); +} + +static int encx24j600_receive_packet(struct encx24j600_priv *priv, + struct rsv *rsv) +{ + struct net_device *dev = priv->ndev; + struct sk_buff *skb = netdev_alloc_skb(dev, rsv->len + NET_IP_ALIGN); + if (!skb) { + pr_err_ratelimited("RX: OOM: packet dropped\n"); + dev->stats.rx_dropped++; + return -ENOMEM; + } + skb_reserve(skb, NET_IP_ALIGN); + encx24j600_raw_read(priv, RRXDATA, skb_put(skb, rsv->len), rsv->len); + + if (netif_msg_pktdata(priv)) + dump_packet("RX", skb->len, skb->data); + + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + skb->ip_summed = CHECKSUM_COMPLETE; + + /* Maintain stats */ + dev->stats.rx_packets++; + dev->stats.rx_bytes += rsv->len; + priv->next_packet = rsv->next_packet; + + netif_rx(skb); + + return 0; +} + +static void encx24j600_rx_packets(struct encx24j600_priv *priv, u8 packet_count) +{ + struct net_device *dev = priv->ndev; + + while (packet_count--) { + struct rsv rsv; + u16 newrxtail; + + encx24j600_write_reg(priv, ERXRDPT, priv->next_packet); + encx24j600_raw_read(priv, RRXDATA, (u8 *)&rsv, sizeof(rsv)); + + if (netif_msg_rx_status(priv)) + encx24j600_dump_rsv(priv, __func__, &rsv); + + if (!RSV_GETBIT(rsv.rxstat, RSV_RXOK) || + (rsv.len > MAX_FRAMELEN)) { + netif_err(priv, rx_err, dev, "RX Error %04x\n", + rsv.rxstat); + dev->stats.rx_errors++; + + if (RSV_GETBIT(rsv.rxstat, RSV_CRCERROR)) + dev->stats.rx_crc_errors++; + if (RSV_GETBIT(rsv.rxstat, RSV_LENCHECKERR)) + dev->stats.rx_frame_errors++; + if (rsv.len > MAX_FRAMELEN) + dev->stats.rx_over_errors++; + } else { + encx24j600_receive_packet(priv, &rsv); + } + + newrxtail = priv->next_packet - 2; + if (newrxtail == ENC_RX_BUF_START) + newrxtail = SRAM_SIZE - 2; + + encx24j600_cmd(priv, SETPKTDEC); + encx24j600_write_reg(priv, ERXTAIL, newrxtail); + } +} + +static irqreturn_t encx24j600_isr(int irq, void *dev_id) +{ + struct encx24j600_priv *priv = dev_id; + struct net_device *dev = priv->ndev; + int eir; + + /* Clear interrupts */ + encx24j600_cmd(priv, CLREIE); + + eir = encx24j600_read_reg(priv, EIR); + + if (eir & LINKIF) + encx24j600_int_link_handler(priv); + + if (eir & TXIF) + encx24j600_tx_complete(priv, false); + + if (eir & TXABTIF) + encx24j600_tx_complete(priv, true); + + if (eir & RXABTIF) { + if (eir & PCFULIF) { + /* Packet counter is full */ + netif_err(priv, rx_err, dev, "Packet counter full\n"); + } + dev->stats.rx_dropped++; + encx24j600_clr_bits(priv, EIR, RXABTIF); + } + + if (eir & PKTIF) { + u8 packet_count; + + mutex_lock(&priv->lock); + + packet_count = encx24j600_read_reg(priv, ESTAT) & 0xff; + while (packet_count) { + encx24j600_rx_packets(priv, packet_count); + packet_count = encx24j600_read_reg(priv, ESTAT) & 0xff; + } + + mutex_unlock(&priv->lock); + } + + /* Enable interrupts */ + encx24j600_cmd(priv, SETEIE); + + return IRQ_HANDLED; +} + +static int encx24j600_soft_reset(struct encx24j600_priv *priv) +{ + int ret = 0; + int timeout; + u16 eudast; + + /* Write and verify a test value to EUDAST */ + regcache_cache_bypass(priv->ctx.regmap, true); + timeout = 10; + do { + encx24j600_write_reg(priv, EUDAST, EUDAST_TEST_VAL); + eudast = encx24j600_read_reg(priv, EUDAST); + usleep_range(25, 100); + } while ((eudast != EUDAST_TEST_VAL) && --timeout); + regcache_cache_bypass(priv->ctx.regmap, false); + + if (timeout == 0) { + ret = -ETIMEDOUT; + goto err_out; + } + + /* Wait for CLKRDY to become set */ + timeout = 10; + while (!(encx24j600_read_reg(priv, ESTAT) & CLKRDY) && --timeout) + usleep_range(25, 100); + + if (timeout == 0) { + ret = -ETIMEDOUT; + goto err_out; + } + + /* Issue a System Reset command */ + encx24j600_cmd(priv, SETETHRST); + usleep_range(25, 100); + + /* Confirm that EUDAST has 0000h after system reset */ + if (encx24j600_read_reg(priv, EUDAST) != 0) { + ret = -EINVAL; + goto err_out; + } + + /* Wait for PHY register and status bits to become available */ + usleep_range(256, 1000); + +err_out: + return ret; +} + +static int encx24j600_hw_reset(struct encx24j600_priv *priv) +{ + int ret; + + mutex_lock(&priv->lock); + ret = encx24j600_soft_reset(priv); + mutex_unlock(&priv->lock); + + return ret; +} + +static void encx24j600_reset_hw_tx(struct encx24j600_priv *priv) +{ + encx24j600_set_bits(priv, ECON2, TXRST); + encx24j600_clr_bits(priv, ECON2, TXRST); +} + +static void encx24j600_hw_init_tx(struct encx24j600_priv *priv) +{ + /* Reset TX */ + encx24j600_reset_hw_tx(priv); + + /* Clear the TXIF flag if were previously set */ + encx24j600_clr_bits(priv, EIR, TXIF | TXABTIF); + + /* Write the Tx Buffer pointer */ + encx24j600_write_reg(priv, EGPWRPT, ENC_TX_BUF_START); +} + +static void encx24j600_hw_init_rx(struct encx24j600_priv *priv) +{ + encx24j600_cmd(priv, DISABLERX); + + /* Set up RX packet start address in the SRAM */ + encx24j600_write_reg(priv, ERXST, ENC_RX_BUF_START); + + /* Preload the RX Data pointer to the beginning of the RX area */ + encx24j600_write_reg(priv, ERXRDPT, ENC_RX_BUF_START); + + priv->next_packet = ENC_RX_BUF_START; + + /* Set up RX end address in the SRAM */ + encx24j600_write_reg(priv, ERXTAIL, ENC_SRAM_SIZE - 2); + + /* Reset the user data pointers */ + encx24j600_write_reg(priv, EUDAST, ENC_SRAM_SIZE); + encx24j600_write_reg(priv, EUDAND, ENC_SRAM_SIZE + 1); + + /* Set Max Frame length */ + encx24j600_write_reg(priv, MAMXFL, MAX_FRAMELEN); +} + +static void encx24j600_dump_config(struct encx24j600_priv *priv, + const char *msg) +{ + pr_info(DRV_NAME ": %s\n", msg); + + /* CHIP configuration */ + pr_info(DRV_NAME " ECON1: %04X\n", encx24j600_read_reg(priv, ECON1)); + pr_info(DRV_NAME " ECON2: %04X\n", encx24j600_read_reg(priv, ECON2)); + pr_info(DRV_NAME " ERXFCON: %04X\n", encx24j600_read_reg(priv, + ERXFCON)); + pr_info(DRV_NAME " ESTAT: %04X\n", encx24j600_read_reg(priv, ESTAT)); + pr_info(DRV_NAME " EIR: %04X\n", encx24j600_read_reg(priv, EIR)); + pr_info(DRV_NAME " EIDLED: %04X\n", encx24j600_read_reg(priv, EIDLED)); + + /* MAC layer configuration */ + pr_info(DRV_NAME " MACON1: %04X\n", encx24j600_read_reg(priv, MACON1)); + pr_info(DRV_NAME " MACON2: %04X\n", encx24j600_read_reg(priv, MACON2)); + pr_info(DRV_NAME " MAIPG: %04X\n", encx24j600_read_reg(priv, MAIPG)); + pr_info(DRV_NAME " MACLCON: %04X\n", encx24j600_read_reg(priv, + MACLCON)); + pr_info(DRV_NAME " MABBIPG: %04X\n", encx24j600_read_reg(priv, + MABBIPG)); + + /* PHY configuation */ + pr_info(DRV_NAME " PHCON1: %04X\n", encx24j600_read_phy(priv, PHCON1)); + pr_info(DRV_NAME " PHCON2: %04X\n", encx24j600_read_phy(priv, PHCON2)); + pr_info(DRV_NAME " PHANA: %04X\n", encx24j600_read_phy(priv, PHANA)); + pr_info(DRV_NAME " PHANLPA: %04X\n", encx24j600_read_phy(priv, + PHANLPA)); + pr_info(DRV_NAME " PHANE: %04X\n", encx24j600_read_phy(priv, PHANE)); + pr_info(DRV_NAME " PHSTAT1: %04X\n", encx24j600_read_phy(priv, + PHSTAT1)); + pr_info(DRV_NAME " PHSTAT2: %04X\n", encx24j600_read_phy(priv, + PHSTAT2)); + pr_info(DRV_NAME " PHSTAT3: %04X\n", encx24j600_read_phy(priv, + PHSTAT3)); +} + +static void encx24j600_set_rxfilter_mode(struct encx24j600_priv *priv) +{ + switch (priv->rxfilter) { + case RXFILTER_PROMISC: + encx24j600_set_bits(priv, MACON1, PASSALL); + encx24j600_write_reg(priv, ERXFCON, UCEN | MCEN | NOTMEEN); + break; + case RXFILTER_MULTI: + encx24j600_clr_bits(priv, MACON1, PASSALL); + encx24j600_write_reg(priv, ERXFCON, UCEN | CRCEN | BCEN | MCEN); + break; + case RXFILTER_NORMAL: + default: + encx24j600_clr_bits(priv, MACON1, PASSALL); + encx24j600_write_reg(priv, ERXFCON, UCEN | CRCEN | BCEN); + break; + } +} + +static int encx24j600_hw_init(struct encx24j600_priv *priv) +{ + struct net_device *dev = priv->ndev; + int ret = 0; + u16 eidled; + u16 macon2; + + priv->hw_enabled = false; + + eidled = encx24j600_read_reg(priv, EIDLED); + if (((eidled & DEVID_MASK) >> DEVID_SHIFT) != ENCX24J600_DEV_ID) { + ret = -EINVAL; + goto err_out; + } + + netif_info(priv, drv, dev, "Silicon rev ID: 0x%02x\n", + (eidled & REVID_MASK) >> REVID_SHIFT); + + /* PHY Leds: link status, + * LEDA: Link + transmit/receive events + * LEDB: Link State + colision events + */ + encx24j600_update_reg(priv, EIDLED, 0xbc00, 0xbc00); + + /* Loopback disabled */ + encx24j600_write_reg(priv, MACON1, 0x9); + + /* interpacket gap value */ + encx24j600_write_reg(priv, MAIPG, 0x0c12); + + /* Write the auto negotiation pattern */ + encx24j600_write_phy(priv, PHANA, PHANA_DEFAULT); + + encx24j600_update_phcon1(priv); + encx24j600_check_link_status(priv); + + macon2 = MACON2_RSV1 | TXCRCEN | PADCFG0 | PADCFG2 | MACON2_DEFER; + if ((priv->autoneg == AUTONEG_DISABLE) && priv->full_duplex) + macon2 |= FULDPX; + + encx24j600_set_bits(priv, MACON2, macon2); + + priv->rxfilter = RXFILTER_NORMAL; + encx24j600_set_rxfilter_mode(priv); + + /* Program the Maximum frame length */ + encx24j600_write_reg(priv, MAMXFL, MAX_FRAMELEN); + + /* Init Tx pointers */ + encx24j600_hw_init_tx(priv); + + /* Init Rx pointers */ + encx24j600_hw_init_rx(priv); + + if (netif_msg_hw(priv)) + encx24j600_dump_config(priv, "Hw is initialized"); + +err_out: + return ret; +} + +static void encx24j600_hw_enable(struct encx24j600_priv *priv) +{ + /* Clear the interrupt flags in case was set */ + encx24j600_clr_bits(priv, EIR, (PCFULIF | RXABTIF | TXABTIF | TXIF | + PKTIF | LINKIF)); + + /* Enable the interrupts */ + encx24j600_write_reg(priv, EIE, (PCFULIE | RXABTIE | TXABTIE | TXIE | + PKTIE | LINKIE | INTIE)); + + /* Enable RX */ + encx24j600_cmd(priv, ENABLERX); + + priv->hw_enabled = true; +} + +static void encx24j600_hw_disable(struct encx24j600_priv *priv) +{ + /* Disable all interrupts */ + encx24j600_write_reg(priv, EIE, 0); + + /* Disable RX */ + encx24j600_cmd(priv, DISABLERX); + + priv->hw_enabled = false; +} + +static int encx24j600_setlink(struct net_device *dev, u8 autoneg, u16 speed, + u8 duplex) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + int ret = 0; + + if (!priv->hw_enabled) { + /* link is in low power mode now; duplex setting + * will take effect on next encx24j600_hw_init() + */ + if (speed == SPEED_10 || speed == SPEED_100) { + priv->autoneg = (autoneg == AUTONEG_ENABLE); + priv->full_duplex = (duplex == DUPLEX_FULL); + priv->speed = (speed == SPEED_100); + } else { + netif_warn(priv, link, dev, "unsupported link speed setting\n"); + /*speeds other than SPEED_10 and SPEED_100 */ + /*are not supported by chip */ + ret = -EOPNOTSUPP; + } + } else { + netif_warn(priv, link, dev, "Warning: hw must be disabled to set link mode\n"); + ret = -EBUSY; + } + return ret; +} + +static void encx24j600_hw_get_macaddr(struct encx24j600_priv *priv, + unsigned char *ethaddr) +{ + unsigned short val; + + val = encx24j600_read_reg(priv, MAADR1); + + ethaddr[0] = val & 0x00ff; + ethaddr[1] = (val & 0xff00) >> 8; + + val = encx24j600_read_reg(priv, MAADR2); + + ethaddr[2] = val & 0x00ffU; + ethaddr[3] = (val & 0xff00U) >> 8; + + val = encx24j600_read_reg(priv, MAADR3); + + ethaddr[4] = val & 0x00ffU; + ethaddr[5] = (val & 0xff00U) >> 8; +} + +/* Program the hardware MAC address from dev->dev_addr.*/ +static int encx24j600_set_hw_macaddr(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + if (priv->hw_enabled) { + netif_info(priv, drv, dev, "Hardware must be disabled to set Mac address\n"); + return -EBUSY; + } + + mutex_lock(&priv->lock); + + netif_info(priv, drv, dev, "%s: Setting MAC address to %pM\n", + dev->name, dev->dev_addr); + + encx24j600_write_reg(priv, MAADR3, (dev->dev_addr[4] | + dev->dev_addr[5] << 8)); + encx24j600_write_reg(priv, MAADR2, (dev->dev_addr[2] | + dev->dev_addr[3] << 8)); + encx24j600_write_reg(priv, MAADR1, (dev->dev_addr[0] | + dev->dev_addr[1] << 8)); + + mutex_unlock(&priv->lock); + + return 0; +} + +/* Store the new hardware address in dev->dev_addr, and update the MAC.*/ +static int encx24j600_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *address = addr; + + if (netif_running(dev)) + return -EBUSY; + if (!is_valid_ether_addr(address->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(dev->dev_addr, address->sa_data, dev->addr_len); + return encx24j600_set_hw_macaddr(dev); +} + +static int encx24j600_open(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + int ret = request_threaded_irq(priv->ctx.spi->irq, NULL, encx24j600_isr, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + DRV_NAME, priv); + if (unlikely(ret < 0)) { + netdev_err(dev, "request irq %d failed (ret = %d)\n", + priv->ctx.spi->irq, ret); + return ret; + } + + encx24j600_hw_disable(priv); + encx24j600_hw_init(priv); + encx24j600_hw_enable(priv); + netif_start_queue(dev); + + return 0; +} + +static int encx24j600_stop(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + netif_stop_queue(dev); + free_irq(priv->ctx.spi->irq, priv); + return 0; +} + +static void encx24j600_setrx_proc(struct kthread_work *ws) +{ + struct encx24j600_priv *priv = + container_of(ws, struct encx24j600_priv, setrx_work); + + mutex_lock(&priv->lock); + encx24j600_set_rxfilter_mode(priv); + mutex_unlock(&priv->lock); +} + +static void encx24j600_set_multicast_list(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + int oldfilter = priv->rxfilter; + + if (dev->flags & IFF_PROMISC) { + netif_dbg(priv, link, dev, "promiscuous mode\n"); + priv->rxfilter = RXFILTER_PROMISC; + } else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev)) { + netif_dbg(priv, link, dev, "%smulticast mode\n", + (dev->flags & IFF_ALLMULTI) ? "all-" : ""); + priv->rxfilter = RXFILTER_MULTI; + } else { + netif_dbg(priv, link, dev, "normal mode\n"); + priv->rxfilter = RXFILTER_NORMAL; + } + + if (oldfilter != priv->rxfilter) + queue_kthread_work(&priv->kworker, &priv->setrx_work); +} + +static void encx24j600_hw_tx(struct encx24j600_priv *priv) +{ + struct net_device *dev = priv->ndev; + netif_info(priv, tx_queued, dev, "TX Packet Len:%d\n", + priv->tx_skb->len); + + if (netif_msg_pktdata(priv)) + dump_packet("TX", priv->tx_skb->len, priv->tx_skb->data); + + if (encx24j600_read_reg(priv, EIR) & TXABTIF) + /* Last transmition aborted due to error. Reset TX interface */ + encx24j600_reset_hw_tx(priv); + + /* Clear the TXIF flag if were previously set */ + encx24j600_clr_bits(priv, EIR, TXIF); + + /* Set the data pointer to the TX buffer address in the SRAM */ + encx24j600_write_reg(priv, EGPWRPT, ENC_TX_BUF_START); + + /* Copy the packet into the SRAM */ + encx24j600_raw_write(priv, WGPDATA, (u8 *)priv->tx_skb->data, + priv->tx_skb->len); + + /* Program the Tx buffer start pointer */ + encx24j600_write_reg(priv, ETXST, ENC_TX_BUF_START); + + /* Program the packet length */ + encx24j600_write_reg(priv, ETXLEN, priv->tx_skb->len); + + /* Start the transmission */ + encx24j600_cmd(priv, SETTXRTS); +} + +static void encx24j600_tx_proc(struct kthread_work *ws) +{ + struct encx24j600_priv *priv = + container_of(ws, struct encx24j600_priv, tx_work); + + mutex_lock(&priv->lock); + encx24j600_hw_tx(priv); + mutex_unlock(&priv->lock); +} + +static netdev_tx_t encx24j600_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + netif_stop_queue(dev); + + /* save the timestamp */ + dev->trans_start = jiffies; + + /* Remember the skb for deferred processing */ + priv->tx_skb = skb; + + queue_kthread_work(&priv->kworker, &priv->tx_work); + + return NETDEV_TX_OK; +} + +/* Deal with a transmit timeout */ +static void encx24j600_tx_timeout(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + netif_err(priv, tx_err, dev, "TX timeout at %ld, latency %ld\n", + jiffies, jiffies - dev->trans_start); + + dev->stats.tx_errors++; + netif_wake_queue(dev); + return; +} + +static int encx24j600_get_regs_len(struct net_device *dev) +{ + return SFR_REG_COUNT; +} + +static void encx24j600_get_regs(struct net_device *dev, + struct ethtool_regs *regs, void *p) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + u16 *buff = p; + u8 reg; + + regs->version = 1; + mutex_lock(&priv->lock); + for (reg = 0; reg < SFR_REG_COUNT; reg += 2) { + unsigned int val = 0; + /* ignore errors for unreadable registers */ + regmap_read(priv->ctx.regmap, reg, &val); + buff[reg] = val & 0xffff; + } + mutex_unlock(&priv->lock); +} + +static void encx24j600_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, dev_name(dev->dev.parent), + sizeof(info->bus_info)); +} + +static int encx24j600_get_settings(struct net_device *dev, + struct ethtool_cmd *cmd) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + cmd->transceiver = XCVR_INTERNAL; + cmd->supported = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg | SUPPORTED_TP; + + ethtool_cmd_speed_set(cmd, priv->speed); + cmd->duplex = priv->full_duplex ? DUPLEX_FULL : DUPLEX_HALF; + cmd->port = PORT_TP; + cmd->autoneg = priv->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE; + + return 0; +} + +static int encx24j600_set_settings(struct net_device *dev, + struct ethtool_cmd *cmd) +{ + return encx24j600_setlink(dev, cmd->autoneg, + ethtool_cmd_speed(cmd), cmd->duplex); +} + +static u32 encx24j600_get_msglevel(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + return priv->msg_enable; +} + +static void encx24j600_set_msglevel(struct net_device *dev, u32 val) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + priv->msg_enable = val; +} + +static const struct ethtool_ops encx24j600_ethtool_ops = { + .get_settings = encx24j600_get_settings, + .set_settings = encx24j600_set_settings, + .get_drvinfo = encx24j600_get_drvinfo, + .get_msglevel = encx24j600_get_msglevel, + .set_msglevel = encx24j600_set_msglevel, + .get_regs_len = encx24j600_get_regs_len, + .get_regs = encx24j600_get_regs, +}; + +static const struct net_device_ops encx24j600_netdev_ops = { + .ndo_open = encx24j600_open, + .ndo_stop = encx24j600_stop, + .ndo_start_xmit = encx24j600_tx, + .ndo_set_rx_mode = encx24j600_set_multicast_list, + .ndo_set_mac_address = encx24j600_set_mac_address, + .ndo_tx_timeout = encx24j600_tx_timeout, + .ndo_validate_addr = eth_validate_addr, +}; + +static int encx24j600_spi_probe(struct spi_device *spi) +{ + int ret; + + struct net_device *ndev; + struct encx24j600_priv *priv; + + ndev = alloc_etherdev(sizeof(struct encx24j600_priv)); + + if (!ndev) { + ret = -ENOMEM; + goto error_out; + } + + priv = netdev_priv(ndev); + spi_set_drvdata(spi, priv); + dev_set_drvdata(&spi->dev, priv); + SET_NETDEV_DEV(ndev, &spi->dev); + + priv->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE); + priv->ndev = ndev; + + /* Default configuration PHY configuration */ + priv->full_duplex = true; + priv->autoneg = AUTONEG_ENABLE; + priv->speed = SPEED_100; + + priv->ctx.spi = spi; + devm_regmap_init_encx24j600(&spi->dev, &priv->ctx); + ndev->irq = spi->irq; + ndev->netdev_ops = &encx24j600_netdev_ops; + + mutex_init(&priv->lock); + + /* Reset device and check if it is connected */ + if (encx24j600_hw_reset(priv)) { + netif_err(priv, probe, ndev, + DRV_NAME ": Chip is not detected\n"); + ret = -EIO; + goto out_free; + } + + /* Initialize the device HW to the consistent state */ + if (encx24j600_hw_init(priv)) { + netif_err(priv, probe, ndev, + DRV_NAME ": HW initialization error\n"); + ret = -EIO; + goto out_free; + } + + init_kthread_worker(&priv->kworker); + init_kthread_work(&priv->tx_work, encx24j600_tx_proc); + init_kthread_work(&priv->setrx_work, encx24j600_setrx_proc); + + priv->kworker_task = kthread_run(kthread_worker_fn, &priv->kworker, + "encx24j600"); + + if (IS_ERR(priv->kworker_task)) { + ret = PTR_ERR(priv->kworker_task); + goto out_free; + } + + /* Get the MAC address from the chip */ + encx24j600_hw_get_macaddr(priv, ndev->dev_addr); + + ndev->ethtool_ops = &encx24j600_ethtool_ops; + + ret = register_netdev(ndev); + if (unlikely(ret)) { + netif_err(priv, probe, ndev, "Error %d initializing card encx24j600 card\n", + ret); + goto out_free; + } + + netif_info(priv, drv, priv->ndev, "MAC address %pM\n", ndev->dev_addr); + + return ret; + +out_free: + free_netdev(ndev); + +error_out: + return ret; +} + +static int encx24j600_spi_remove(struct spi_device *spi) +{ + struct encx24j600_priv *priv = dev_get_drvdata(&spi->dev); + + unregister_netdev(priv->ndev); + + free_netdev(priv->ndev); + + return 0; +} + +static const struct spi_device_id encx24j600_spi_id_table = { + .name = "encx24j600" +}; + +static struct spi_driver encx24j600_spi_net_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .bus = &spi_bus_type, + }, + .probe = encx24j600_spi_probe, + .remove = encx24j600_spi_remove, + .id_table = &encx24j600_spi_id_table, +}; + +static int __init encx24j600_init(void) +{ + return spi_register_driver(&encx24j600_spi_net_driver); +} +module_init(encx24j600_init); + +static void encx24j600_exit(void) +{ + spi_unregister_driver(&encx24j600_spi_net_driver); +} +module_exit(encx24j600_exit); + +MODULE_DESCRIPTION(DRV_NAME " ethernet driver"); +MODULE_AUTHOR("Jon Ringle "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:" DRV_NAME); diff --git a/drivers/net/ethernet/microchip/encx24j600_hw.h b/drivers/net/ethernet/microchip/encx24j600_hw.h new file mode 100644 index 0000000..4be73d5 --- /dev/null +++ b/drivers/net/ethernet/microchip/encx24j600_hw.h @@ -0,0 +1,437 @@ +/** + * encx24j600_hw.h: Register definitions + * + */ + +#ifndef _ENCX24J600_HW_H +#define _ENCX24J600_HW_H + +struct encx24j600_context { + struct spi_device *spi; + struct regmap *regmap; + struct regmap *phymap; + struct mutex mutex; /* mutex to protect access to regmap */ + int bank; +}; + +void devm_regmap_init_encx24j600(struct device *dev, + struct encx24j600_context *ctx); + +/* Single-byte instructions */ +#define BANK_SELECT(bank) (0xC0 | ((bank & (BANK_MASK >> BANK_SHIFT)) << 1)) +#define B0SEL 0xC0 /* Bank 0 Select */ +#define B1SEL 0xC2 /* Bank 1 Select */ +#define B2SEL 0xC4 /* Bank 2 Select */ +#define B3SEL 0xC6 /* Bank 3 Select */ +#define SETETHRST 0xCA /* System Reset */ +#define FCDISABLE 0xE0 /* Flow Control Disable */ +#define FCSINGLE 0xE2 /* Flow Control Single */ +#define FCMULTIPLE 0xE4 /* Flow Control Multiple */ +#define FCCLEAR 0xE6 /* Flow Control Clear */ +#define SETPKTDEC 0xCC /* Decrement Packet Counter */ +#define DMASTOP 0xD2 /* DMA Stop */ +#define DMACKSUM 0xD8 /* DMA Start Checksum */ +#define DMACKSUMS 0xDA /* DMA Start Checksum with Seed */ +#define DMACOPY 0xDC /* DMA Start Copy */ +#define DMACOPYS 0xDE /* DMA Start Copy and Checksum with Seed */ +#define SETTXRTS 0xD4 /* Request Packet Transmission */ +#define ENABLERX 0xE8 /* Enable RX */ +#define DISABLERX 0xEA /* Disable RX */ +#define SETEIE 0xEC /* Enable Interrupts */ +#define CLREIE 0xEE /* Disable Interrupts */ + +/* Two byte instructions */ +#define RBSEL 0xC8 /* Read Bank Select */ + +/* Three byte instructions */ +#define WGPRDPT 0x60 /* Write EGPRDPT */ +#define RGPRDPT 0x62 /* Read EGPRDPT */ +#define WRXRDPT 0x64 /* Write ERXRDPT */ +#define RRXRDPT 0x66 /* Read ERXRDPT */ +#define WUDARDPT 0x68 /* Write EUDARDPT */ +#define RUDARDPT 0x6A /* Read EUDARDPT */ +#define WGPWRPT 0x6C /* Write EGPWRPT */ +#define RGPWRPT 0x6E /* Read EGPWRPT */ +#define WRXWRPT 0x70 /* Write ERXWRPT */ +#define RRXWRPT 0x72 /* Read ERXWRPT */ +#define WUDAWRPT 0x74 /* Write EUDAWRPT */ +#define RUDAWRPT 0x76 /* Read EUDAWRPT */ + +/* n byte instructions */ +#define RCRCODE 0x00 +#define WCRCODE 0x40 +#define BFSCODE 0x80 +#define BFCCODE 0xA0 +#define RCR(addr) (RCRCODE | (addr & ADDR_MASK)) /* Read Control Register */ +#define WCR(addr) (WCRCODE | (addr & ADDR_MASK)) /* Write Control Register */ +#define RCRU 0x20 /* Read Control Register Unbanked */ +#define WCRU 0x22 /* Write Control Register Unbanked */ +#define BFS(addr) (BFSCODE | (addr & ADDR_MASK)) /* Bit Field Set */ +#define BFC(addr) (BFCCODE | (addr & ADDR_MASK)) /* Bit Field Clear */ +#define BFSU 0x24 /* Bit Field Set Unbanked */ +#define BFCU 0x26 /* Bit Field Clear Unbanked */ +#define RGPDATA 0x28 /* Read EGPDATA */ +#define WGPDATA 0x2A /* Write EGPDATA */ +#define RRXDATA 0x2C /* Read ERXDATA */ +#define WRXDATA 0x2E /* Write ERXDATA */ +#define RUDADATA 0x30 /* Read EUDADATA */ +#define WUDADATA 0x32 /* Write EUDADATA */ + +#define SFR_REG_COUNT 0xA0 + +/* ENC424J600 Control Registers + * Control register definitions are a combination of address + * and bank number + * - Register address (bits 0-4) + * - Bank number (bits 5-6) + */ +#define ADDR_MASK 0x1F +#define BANK_MASK 0x60 +#define BANK_SHIFT 5 + +/* All-bank registers */ +#define EUDAST 0x16 +#define EUDAND 0x18 +#define ESTAT 0x1A +#define EIR 0x1C +#define ECON1 0x1E + +/* Bank 0 registers */ +#define ETXST (0x00 | 0x00) +#define ETXLEN (0x02 | 0x00) +#define ERXST (0x04 | 0x00) +#define ERXTAIL (0x06 | 0x00) +#define ERXHEAD (0x08 | 0x00) +#define EDMAST (0x0A | 0x00) +#define EDMALEN (0x0C | 0x00) +#define EDMADST (0x0E | 0x00) +#define EDMACS (0x10 | 0x00) +#define ETXSTAT (0x12 | 0x00) +#define ETXWIRE (0x14 | 0x00) + +/* Bank 1 registers */ +#define EHT1 (0x00 | 0x20) +#define EHT2 (0x02 | 0x20) +#define EHT3 (0x04 | 0x20) +#define EHT4 (0x06 | 0x20) +#define EPMM1 (0x08 | 0x20) +#define EPMM2 (0x0A | 0x20) +#define EPMM3 (0x0C | 0x20) +#define EPMM4 (0x0E | 0x20) +#define EPMCS (0x10 | 0x20) +#define EPMO (0x12 | 0x20) +#define ERXFCON (0x14 | 0x20) + +/* Bank 2 registers */ +#define MACON1 (0x00 | 0x40) +#define MACON2 (0x02 | 0x40) +#define MABBIPG (0x04 | 0x40) +#define MAIPG (0x06 | 0x40) +#define MACLCON (0x08 | 0x40) +#define MAMXFL (0x0A | 0x40) +#define MICMD (0x12 | 0x40) +#define MIREGADR (0x14 | 0x40) + +/* Bank 3 registers */ +#define MAADR3 (0x00 | 0x60) +#define MAADR2 (0x02 | 0x60) +#define MAADR1 (0x04 | 0x60) +#define MIWR (0x06 | 0x60) +#define MIRD (0x08 | 0x60) +#define MISTAT (0x0A | 0x60) +#define EPAUS (0x0C | 0x60) +#define ECON2 (0x0E | 0x60) +#define ERXWM (0x10 | 0x60) +#define EIE (0x12 | 0x60) +#define EIDLED (0x14 | 0x60) + +/* Unbanked registers */ +#define EGPDATA (0x00 | 0x80) +#define ERXDATA (0x02 | 0x80) +#define EUDADATA (0x04 | 0x80) +#define EGPRDPT (0x06 | 0x80) +#define EGPWRPT (0x08 | 0x80) +#define ERXRDPT (0x0A | 0x80) +#define ERXWRPT (0x0C | 0x80) +#define EUDARDPT (0x0E | 0x80) +#define EUDAWRPT (0x10 | 0x80) + + +/* Register bit definitions */ +/* ESTAT */ +#define INT (1 << 15) +#define FCIDLE (1 << 14) +#define RXBUSY (1 << 13) +#define CLKRDY (1 << 12) +#define PHYDPX (1 << 10) +#define PHYLNK (1 << 8) + +/* EIR */ +#define CRYPTEN (1 << 15) +#define MODEXIF (1 << 14) +#define HASHIF (1 << 13) +#define AESIF (1 << 12) +#define LINKIF (1 << 11) +#define PKTIF (1 << 6) +#define DMAIF (1 << 5) +#define TXIF (1 << 3) +#define TXABTIF (1 << 2) +#define RXABTIF (1 << 1) +#define PCFULIF (1 << 0) + +/* ECON1 */ +#define MODEXST (1 << 15) +#define HASHEN (1 << 14) +#define HASHOP (1 << 13) +#define HASHLST (1 << 12) +#define AESST (1 << 11) +#define AESOP1 (1 << 10) +#define AESOP0 (1 << 9) +#define PKTDEC (1 << 8) +#define FCOP1 (1 << 7) +#define FCOP0 (1 << 6) +#define DMAST (1 << 5) +#define DMACPY (1 << 4) +#define DMACSSD (1 << 3) +#define DMANOCS (1 << 2) +#define TXRTS (1 << 1) +#define RXEN (1 << 0) + +/* ETXSTAT */ +#define LATECOL (1 << 10) +#define MAXCOL (1 << 9) +#define EXDEFER (1 << 8) +#define ETXSTATL_DEFER (1 << 7) +#define CRCBAD (1 << 4) +#define COLCNT_MASK 0xF + +/* ERXFCON */ +#define HTEN (1 << 15) +#define MPEN (1 << 14) +#define NOTPM (1 << 12) +#define PMEN3 (1 << 11) +#define PMEN2 (1 << 10) +#define PMEN1 (1 << 9) +#define PMEN0 (1 << 8) +#define CRCEEN (1 << 7) +#define CRCEN (1 << 6) +#define RUNTEEN (1 << 5) +#define RUNTEN (1 << 4) +#define UCEN (1 << 3) +#define NOTMEEN (1 << 2) +#define MCEN (1 << 1) +#define BCEN (1 << 0) + +/* MACON1 */ +#define LOOPBK (1 << 4) +#define RXPAUS (1 << 2) +#define PASSALL (1 << 1) + +/* MACON2 */ +#define MACON2_DEFER (1 << 14) +#define BPEN (1 << 13) +#define NOBKOFF (1 << 12) +#define PADCFG2 (1 << 7) +#define PADCFG1 (1 << 6) +#define PADCFG0 (1 << 5) +#define TXCRCEN (1 << 4) +#define PHDREN (1 << 3) +#define HFRMEN (1 << 2) +#define MACON2_RSV1 (1 << 1) +#define FULDPX (1 << 0) + +/* MAIPG */ +/* value of the high byte is given by the reserved bits, + * value of the low byte is recomended setting of the + * IPG parameter. + */ +#define MAIPGH_VAL 0x0C +#define MAIPGL_VAL 0x12 + +/* MIREGADRH */ +#define MIREGADR_VAL (1 << 8) + +/* MIREGADRL */ +#define PHREG_MASK 0x1F + +/* MICMD */ +#define MIISCAN (1 << 1) +#define MIIRD (1 << 0) + +/* MISTAT */ +#define NVALID (1 << 2) +#define SCAN (1 << 1) +#define BUSY (1 << 0) + +/* ECON2 */ +#define ETHEN (1 << 15) +#define STRCH (1 << 14) +#define TXMAC (1 << 13) +#define SHA1MD5 (1 << 12) +#define COCON3 (1 << 11) +#define COCON2 (1 << 10) +#define COCON1 (1 << 9) +#define COCON0 (1 << 8) +#define AUTOFC (1 << 7) +#define TXRST (1 << 6) +#define RXRST (1 << 5) +#define ETHRST (1 << 4) +#define MODLEN1 (1 << 3) +#define MODLEN0 (1 << 2) +#define AESLEN1 (1 << 1) +#define AESLEN0 (1 << 0) + +/* EIE */ +#define INTIE (1 << 15) +#define MODEXIE (1 << 14) +#define HASHIE (1 << 13) +#define AESIE (1 << 12) +#define LINKIE (1 << 11) +#define PKTIE (1 << 6) +#define DMAIE (1 << 5) +#define TXIE (1 << 3) +#define TXABTIE (1 << 2) +#define RXABTIE (1 << 1) +#define PCFULIE (1 << 0) + +/* EIDLED */ +#define LACFG3 (1 << 15) +#define LACFG2 (1 << 14) +#define LACFG1 (1 << 13) +#define LACFG0 (1 << 12) +#define LBCFG3 (1 << 11) +#define LBCFG2 (1 << 10) +#define LBCFG1 (1 << 9) +#define LBCFG0 (1 << 8) +#define DEVID_SHIFT 5 +#define DEVID_MASK (0x7 << DEVID_SHIFT) +#define REVID_SHIFT 0 +#define REVID_MASK (0x1F << REVID_SHIFT) + +/* PHY registers */ +#define PHCON1 0x00 +#define PHSTAT1 0x01 +#define PHANA 0x04 +#define PHANLPA 0x05 +#define PHANE 0x06 +#define PHCON2 0x11 +#define PHSTAT2 0x1B +#define PHSTAT3 0x1F + +/* PHCON1 */ +#define PRST (1 << 15) +#define PLOOPBK (1 << 14) +#define SPD100 (1 << 13) +#define ANEN (1 << 12) +#define PSLEEP (1 << 11) +#define RENEG (1 << 9) +#define PFULDPX (1 << 8) + +/* PHSTAT1 */ +#define FULL100 (1 << 14) +#define HALF100 (1 << 13) +#define FULL10 (1 << 12) +#define HALF10 (1 << 11) +#define ANDONE (1 << 5) +#define LRFAULT (1 << 4) +#define ANABLE (1 << 3) +#define LLSTAT (1 << 2) +#define EXTREGS (1 << 0) + +/* PHSTAT2 */ +#define PLRITY (1 << 4) + +/* PHSTAT3 */ +#define PHY3SPD100 (1 << 3) +#define PHY3DPX (1 << 4) +#define SPDDPX_SHIFT 2 +#define SPDDPX_MASK (0x7 << SPDDPX_SHIFT) + +/* PHANA */ +/* Default value for PHY initialization*/ +#define PHANA_DEFAULT 0x05E1 + +/* PHANE */ +#define PDFLT (1 << 4) +#define LPARCD (1 << 1) +#define LPANABL (1 << 0) + +#define EUDAST_TEST_VAL 0x1234 + +#define TSV_SIZE 7 + +#define ENCX24J600_DEV_ID 0x1 + +/* Configuration */ + +/* Led is on when the link is present and driven low + * temporarily when packet is TX'd or RX'd + */ +#define LED_A_SETTINGS 0xC + +/* Led is on if the link is in 100 Mbps mode */ +#define LED_B_SETTINGS 0x8 + +/* maximum ethernet frame length + * Currently not used as a limit anywhere + * (we're using the "huge frame enable" feature of + * enc424j600). + */ +#define MAX_FRAMELEN 1518 + +/* Size in bytes of the receive buffer in enc424j600. + * Must be word aligned (even). + */ +#define RX_BUFFER_SIZE (15 * MAX_FRAMELEN) + +/* Start of the general purpose area in sram */ +#define SRAM_GP_START 0x0 + +/* SRAM size */ +#define SRAM_SIZE 0x6000 + +/* Start of the receive buffer */ +#define ERXST_VAL (SRAM_SIZE - RX_BUFFER_SIZE) + +#define RSV_RXLONGEVDROPEV 16 +#define RSV_CARRIEREV 18 +#define RSV_CRCERROR 20 +#define RSV_LENCHECKERR 21 +#define RSV_LENOUTOFRANGE 22 +#define RSV_RXOK 23 +#define RSV_RXMULTICAST 24 +#define RSV_RXBROADCAST 25 +#define RSV_DRIBBLENIBBLE 26 +#define RSV_RXCONTROLFRAME 27 +#define RSV_RXPAUSEFRAME 28 +#define RSV_RXUNKNOWNOPCODE 29 +#define RSV_RXTYPEVLAN 30 + +#define RSV_RUNTFILTERMATCH 31 +#define RSV_NOTMEFILTERMATCH 32 +#define RSV_HASHFILTERMATCH 33 +#define RSV_MAGICPKTFILTERMATCH 34 +#define RSV_PTRNMTCHFILTERMATCH 35 +#define RSV_UNICASTFILTERMATCH 36 + +#define RSV_SIZE 8 +#define RSV_BITMASK(x) (1 << ((x) - 16)) +#define RSV_GETBIT(x, y) (((x) & RSV_BITMASK(y)) ? 1 : 0) + +struct rsv { + u16 next_packet; + u16 len; + u32 rxstat; +}; + +/* Put RX buffer at 0 as suggested by the Errata datasheet */ + +#define RXSTART_INIT ERXST_VAL +#define RXEND_INIT 0x5FFF + +int regmap_encx24j600_spi_write(void *context, u8 reg, const u8 *data, + size_t count); +int regmap_encx24j600_spi_read(void *context, u8 reg, u8 *data, size_t count); + + +#endif -- cgit v0.10.2 From dfdd7230c5a2d9d675b4e7d6e111a8ead13cfb11 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 6 Oct 2015 23:53:57 +0200 Subject: net: hns: fix 32-bit build warning The recently added hns driver causes a build warning in ARM allmodconfig builds: drivers/net/ethernet/hisilicon/hns/hnae.c: In function 'handles_show': drivers/net/ethernet/hisilicon/hns/hnae.c:452:13: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] j, (u64)h->qs[i]->io_base); ^ This removes the pointless cast and prints the pointer address using the "%p" format string in all three locations. Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.c b/drivers/net/ethernet/hisilicon/hns/hnae.c index 0a0a9e8..f52e99a 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.c +++ b/drivers/net/ethernet/hisilicon/hns/hnae.c @@ -448,12 +448,12 @@ static ssize_t handles_show(struct device *dev, s += sprintf(buf + s, "handle %d (eport_id=%u from %s):\n", i++, h->eport_id, h->dev->name); for (j = 0; j < h->q_num; j++) { - s += sprintf(buf + s, "\tqueue[%d] on 0x%llx\n", - j, (u64)h->qs[i]->io_base); -#define HANDEL_TX_MSG "\t\ttx_ring on 0x%llx:%u,%u,%u,%u,%u,%llu,%llu\n" + s += sprintf(buf + s, "\tqueue[%d] on %p\n", + j, h->qs[i]->io_base); +#define HANDEL_TX_MSG "\t\ttx_ring on %p:%u,%u,%u,%u,%u,%llu,%llu\n" s += sprintf(buf + s, HANDEL_TX_MSG, - (u64)h->qs[i]->tx_ring.io_base, + h->qs[i]->tx_ring.io_base, h->qs[i]->tx_ring.buf_size, h->qs[i]->tx_ring.desc_num, h->qs[i]->tx_ring.max_desc_num_per_pkt, @@ -462,8 +462,8 @@ static ssize_t handles_show(struct device *dev, h->qs[i]->tx_ring.stats.sw_err_cnt, h->qs[i]->tx_ring.stats.io_err_cnt); s += sprintf(buf + s, - "\t\trx_ring on 0x%llx:%u,%u,%llu,%llu,%llu\n", - (u64)h->qs[i]->rx_ring.io_base, + "\t\trx_ring on %p:%u,%u,%llu,%llu,%llu\n", + h->qs[i]->rx_ring.io_base, h->qs[i]->rx_ring.buf_size, h->qs[i]->rx_ring.desc_num, h->qs[i]->rx_ring.stats.sw_err_cnt, -- cgit v0.10.2 From cfc81b50387086c3a1ca6d2be3c46561edfbc3b5 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 7 Oct 2015 10:16:09 +0200 Subject: bpf, skb_do_redirect: clear sender_cpu before xmit Similar to commit c29390c6dfee ("xps: must clear sender_cpu before forwarding"), we also need to clear the skb->sender_cpu when moving from RX to TX via skb_do_redirect() due to the shared location of napi_id (used on RX) and sender_cpu (used on TX). Fixes: 27b29f63058d ("bpf: add bpf_redirect() helper") Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller diff --git a/net/core/filter.c b/net/core/filter.c index da3e535..8f4603c 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1462,6 +1462,7 @@ int skb_do_redirect(struct sk_buff *skb) return dev_forward_skb(dev, skb); skb->dev = dev; + skb_sender_cpu_clear(skb); return dev_queue_xmit(skb); } -- cgit v0.10.2 From 28335a7445202a3d118145a07d9138e9881ebe18 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 7 Oct 2015 08:40:13 -0700 Subject: net: Do not drop to make_route if oif is l3mdev Commit deaa0a6a930 ("net: Lookup actual route when oif is VRF device") exposed a bug in __ip_route_output_key_hash for VRF devices: on FIB lookup failure if the oif is specified the current logic drops to make_route on the assumption that the route tables are wrong. For VRF/L3 master devices this leads to wrong dst entries and route lookups. For example: $ ip route ls table vrf-red unreachable default broadcast 10.2.1.0 dev eth1 proto kernel scope link src 10.2.1.2 10.2.1.0/24 dev eth1 proto kernel scope link src 10.2.1.2 local 10.2.1.2 dev eth1 proto kernel scope host src 10.2.1.2 broadcast 10.2.1.255 dev eth1 proto kernel scope link src 10.2.1.2 $ ip route get oif vrf-red 1.1.1.1 1.1.1.1 dev vrf-red src 10.0.0.2 cache With this patch: $ ip route get oif vrf-red 1.1.1.1 RTNETLINK answers: No route to host which is the correct response based on the default route Signed-off-by: David Ahern Signed-off-by: David S. Miller diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 4be5ff08..85f184e 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2196,7 +2196,8 @@ struct rtable *__ip_route_output_key_hash(struct net *net, struct flowi4 *fl4, if (err) { res.fi = NULL; res.table = NULL; - if (fl4->flowi4_oif) { + if (fl4->flowi4_oif && + !netif_index_is_l3_master(net, fl4->flowi4_oif)) { /* Apparently, routing tables are wrong. Assume, that the destination is on link. -- cgit v0.10.2 From 4d6a6aed22f91b35c14a6717d42953f260090175 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Fri, 2 Oct 2015 20:28:04 +0200 Subject: 6lowpan: move shared settings to lowpan_netdev_setup This patch moves values for all lowpan interface to the shared implementation of 6lowpan. This patch also quietly fixes the forgotten IFF_NO_QUEUE flag for the bluetooth 6LoWPAN interface. An identically commit is 4afbc0d ("net: 6lowpan: convert to using IFF_NO_QUEUE") which wasn't changed for bluetooth 6lowpan. All 6lowpan interfaces should be virtual with IFF_NO_QUEUE, using EUI64 address length, the mtu size is 1280 (IPV6_MIN_MTU) and the netdev type is ARPHRD_6LOWPAN. Signed-off-by: Alexander Aring Acked-by: Jukka Rissanen Signed-off-by: Marcel Holtmann diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h index c17f556..07db532 100644 --- a/include/net/6lowpan.h +++ b/include/net/6lowpan.h @@ -61,6 +61,8 @@ #define UIP_PROTO_UDP 17 /* ipv6 next header value for UDP */ #define UIP_FRAGH_LEN 8 /* ipv6 fragment header size */ +#define EUI64_ADDR_LEN 8 + #define LOWPAN_NHC_MAX_ID_LEN 1 /* Max IPHC Header len without IPv6 hdr specific inline data. * Useful for getting the "extra" bytes we need at worst case compression. diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c index ae1896f..83b19e0 100644 --- a/net/6lowpan/core.c +++ b/net/6lowpan/core.c @@ -17,6 +17,11 @@ void lowpan_netdev_setup(struct net_device *dev, enum lowpan_lltypes lltype) { + dev->addr_len = EUI64_ADDR_LEN; + dev->type = ARPHRD_6LOWPAN; + dev->mtu = IPV6_MIN_MTU; + dev->priv_flags |= IFF_NO_QUEUE; + lowpan_priv(dev)->lltype = lltype; } EXPORT_SYMBOL(lowpan_netdev_setup); diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 9363f05..db73b8a 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -35,7 +35,6 @@ static struct dentry *lowpan_enable_debugfs; static struct dentry *lowpan_control_debugfs; #define IFACE_NAME_TEMPLATE "bt%d" -#define EUI64_ADDR_LEN 8 struct skb_cb { struct in6_addr addr; @@ -674,13 +673,8 @@ static struct header_ops header_ops = { static void netdev_setup(struct net_device *dev) { - dev->addr_len = EUI64_ADDR_LEN; - dev->type = ARPHRD_6LOWPAN; - dev->hard_header_len = 0; dev->needed_tailroom = 0; - dev->mtu = IPV6_MIN_MTU; - dev->tx_queue_len = 0; dev->flags = IFF_RUNNING | IFF_POINTOPOINT | IFF_MULTICAST; dev->watchdog_timeo = 0; diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c index 44420ed..20c49c7 100644 --- a/net/ieee802154/6lowpan/core.c +++ b/net/ieee802154/6lowpan/core.c @@ -101,13 +101,9 @@ static const struct net_device_ops lowpan_netdev_ops = { static void lowpan_setup(struct net_device *ldev) { - ldev->addr_len = IEEE802154_ADDR_LEN; memset(ldev->broadcast, 0xff, IEEE802154_ADDR_LEN); - ldev->type = ARPHRD_6LOWPAN; /* We need an ipv6hdr as minimum len when calling xmit */ ldev->hard_header_len = sizeof(struct ipv6hdr); - ldev->mtu = IPV6_MIN_MTU; - ldev->priv_flags |= IFF_NO_QUEUE; ldev->flags = IFF_BROADCAST | IFF_MULTICAST; ldev->netdev_ops = &lowpan_netdev_ops; -- cgit v0.10.2 From 46234253b9363894a254844a6550b4cc5f3edfe8 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Thu, 8 Oct 2015 01:20:35 +0200 Subject: net: move net_get_random_once to lib There's no good reason why users outside of networking should not be using this facility, f.e. for initializing their seeds. Therefore, make it accessible from there as get_random_once(). Signed-off-by: Hannes Frederic Sowa Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller diff --git a/include/linux/net.h b/include/linux/net.h index 049d4b0..70ac5e2 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -24,7 +24,8 @@ #include /* For O_CLOEXEC and O_NONBLOCK */ #include #include -#include +#include + #include struct poll_table_struct; @@ -250,22 +251,8 @@ do { \ } while (0) #endif -bool __net_get_random_once(void *buf, int nbytes, bool *done, - struct static_key *done_key); - -#define net_get_random_once(buf, nbytes) \ - ({ \ - bool ___ret = false; \ - static bool ___done = false; \ - static struct static_key ___once_key = \ - STATIC_KEY_INIT_TRUE; \ - if (static_key_true(&___once_key)) \ - ___ret = __net_get_random_once(buf, \ - nbytes, \ - &___done, \ - &___once_key); \ - ___ret; \ - }) +#define net_get_random_once(buf, nbytes) \ + get_random_once((buf), (nbytes)) int kernel_sendmsg(struct socket *sock, struct msghdr *msg, struct kvec *vec, size_t num, size_t len); diff --git a/include/linux/once.h b/include/linux/once.h new file mode 100644 index 0000000..2a83b53 --- /dev/null +++ b/include/linux/once.h @@ -0,0 +1,24 @@ +#ifndef _LINUX_ONCE_H +#define _LINUX_ONCE_H + +#include +#include + +bool __get_random_once(void *buf, int nbytes, bool *done, + struct static_key *once_key); + +#define get_random_once(buf, nbytes) \ + ({ \ + bool ___ret = false; \ + static bool ___done = false; \ + static struct static_key ___once_key = \ + STATIC_KEY_INIT_TRUE; \ + if (static_key_true(&___once_key)) \ + ___ret = __get_random_once((buf), \ + (nbytes), \ + &___done, \ + &___once_key); \ + ___ret; \ + }) + +#endif /* _LINUX_ONCE_H */ diff --git a/lib/Makefile b/lib/Makefile index 13a7c6a..8de3b01 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -26,7 +26,8 @@ obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \ bust_spinlocks.o kasprintf.o bitmap.o scatterlist.o \ gcd.o lcm.o list_sort.o uuid.o flex_array.o iov_iter.o clz_ctz.o \ bsearch.o find_bit.o llist.o memweight.o kfifo.o \ - percpu-refcount.o percpu_ida.o rhashtable.o reciprocal_div.o + percpu-refcount.o percpu_ida.o rhashtable.o reciprocal_div.o \ + once.o obj-y += string_helpers.o obj-$(CONFIG_TEST_STRING_HELPERS) += test-string_helpers.o obj-y += hexdump.o diff --git a/lib/once.c b/lib/once.c new file mode 100644 index 0000000..2d5a7de --- /dev/null +++ b/lib/once.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include + +struct __random_once_work { + struct work_struct work; + struct static_key *key; +}; + +static void __random_once_deferred(struct work_struct *w) +{ + struct __random_once_work *work; + + work = container_of(w, struct __random_once_work, work); + BUG_ON(!static_key_enabled(work->key)); + static_key_slow_dec(work->key); + kfree(work); +} + +static void __random_once_disable_jump(struct static_key *key) +{ + struct __random_once_work *w; + + w = kmalloc(sizeof(*w), GFP_ATOMIC); + if (!w) + return; + + INIT_WORK(&w->work, __random_once_deferred); + w->key = key; + schedule_work(&w->work); +} + +bool __get_random_once(void *buf, int nbytes, bool *done, + struct static_key *once_key) +{ + static DEFINE_SPINLOCK(lock); + unsigned long flags; + + spin_lock_irqsave(&lock, flags); + if (*done) { + spin_unlock_irqrestore(&lock, flags); + return false; + } + + get_random_bytes(buf, nbytes); + *done = true; + spin_unlock_irqrestore(&lock, flags); + + __random_once_disable_jump(once_key); + + return true; +} +EXPORT_SYMBOL(__get_random_once); diff --git a/net/core/utils.c b/net/core/utils.c index 3dffce9..3d17ca8 100644 --- a/net/core/utils.c +++ b/net/core/utils.c @@ -348,52 +348,3 @@ void inet_proto_csum_replace_by_diff(__sum16 *sum, struct sk_buff *skb, } } EXPORT_SYMBOL(inet_proto_csum_replace_by_diff); - -struct __net_random_once_work { - struct work_struct work; - struct static_key *key; -}; - -static void __net_random_once_deferred(struct work_struct *w) -{ - struct __net_random_once_work *work = - container_of(w, struct __net_random_once_work, work); - BUG_ON(!static_key_enabled(work->key)); - static_key_slow_dec(work->key); - kfree(work); -} - -static void __net_random_once_disable_jump(struct static_key *key) -{ - struct __net_random_once_work *w; - - w = kmalloc(sizeof(*w), GFP_ATOMIC); - if (!w) - return; - - INIT_WORK(&w->work, __net_random_once_deferred); - w->key = key; - schedule_work(&w->work); -} - -bool __net_get_random_once(void *buf, int nbytes, bool *done, - struct static_key *once_key) -{ - static DEFINE_SPINLOCK(lock); - unsigned long flags; - - spin_lock_irqsave(&lock, flags); - if (*done) { - spin_unlock_irqrestore(&lock, flags); - return false; - } - - get_random_bytes(buf, nbytes); - *done = true; - spin_unlock_irqrestore(&lock, flags); - - __net_random_once_disable_jump(once_key); - - return true; -} -EXPORT_SYMBOL(__net_get_random_once); -- cgit v0.10.2 From c90aeb948222a7b3d3391d232ec4f50fd8322ad3 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Thu, 8 Oct 2015 01:20:36 +0200 Subject: once: make helper generic for calling functions once Make the get_random_once() helper generic enough, so that functions in general would only be called once, where one user of this is then net_get_random_once(). The only implementation specific call is to get_random_bytes(), all the rest of this *_once() facility would be duplicated among different subsystems otherwise. The new DO_ONCE() helper will be used by prandom() later on, but might also be useful for other scenarios/subsystems as well where a one-time initialization in often-called, possibly fast path code could occur. Signed-off-by: Hannes Frederic Sowa Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller diff --git a/include/linux/once.h b/include/linux/once.h index 2a83b53..285f12c 100644 --- a/include/linux/once.h +++ b/include/linux/once.h @@ -4,21 +4,54 @@ #include #include -bool __get_random_once(void *buf, int nbytes, bool *done, - struct static_key *once_key); +bool __do_once_start(bool *done, unsigned long *flags); +void __do_once_done(bool *done, struct static_key *once_key, + unsigned long *flags); -#define get_random_once(buf, nbytes) \ - ({ \ - bool ___ret = false; \ - static bool ___done = false; \ - static struct static_key ___once_key = \ - STATIC_KEY_INIT_TRUE; \ - if (static_key_true(&___once_key)) \ - ___ret = __get_random_once((buf), \ - (nbytes), \ - &___done, \ - &___once_key); \ - ___ret; \ +/* Call a function exactly once. The idea of DO_ONCE() is to perform + * a function call such as initialization of random seeds, etc, only + * once, where DO_ONCE() can live in the fast-path. After @func has + * been called with the passed arguments, the static key will patch + * out the condition into a nop. DO_ONCE() guarantees type safety of + * arguments! + * + * Not that the following is not equivalent ... + * + * DO_ONCE(func, arg); + * DO_ONCE(func, arg); + * + * ... to this version: + * + * void foo(void) + * { + * DO_ONCE(func, arg); + * } + * + * foo(); + * foo(); + * + * In case the one-time invocation could be triggered from multiple + * places, then a common helper function must be defined, so that only + * a single static key will be placed there! + */ +#define DO_ONCE(func, ...) \ + ({ \ + bool ___ret = false; \ + static bool ___done = false; \ + static struct static_key ___once_key = STATIC_KEY_INIT_TRUE; \ + if (static_key_true(&___once_key)) { \ + unsigned long ___flags; \ + ___ret = __do_once_start(&___done, &___flags); \ + if (unlikely(___ret)) { \ + func(__VA_ARGS__); \ + __do_once_done(&___done, &___once_key, \ + &___flags); \ + } \ + } \ + ___ret; \ }) +#define get_random_once(buf, nbytes) \ + DO_ONCE(get_random_bytes, (buf), (nbytes)) + #endif /* _LINUX_ONCE_H */ diff --git a/lib/once.c b/lib/once.c index 2d5a7de..05c8604 100644 --- a/lib/once.c +++ b/lib/once.c @@ -3,52 +3,60 @@ #include #include -struct __random_once_work { +struct once_work { struct work_struct work; struct static_key *key; }; -static void __random_once_deferred(struct work_struct *w) +static void once_deferred(struct work_struct *w) { - struct __random_once_work *work; + struct once_work *work; - work = container_of(w, struct __random_once_work, work); + work = container_of(w, struct once_work, work); BUG_ON(!static_key_enabled(work->key)); static_key_slow_dec(work->key); kfree(work); } -static void __random_once_disable_jump(struct static_key *key) +static void once_disable_jump(struct static_key *key) { - struct __random_once_work *w; + struct once_work *w; w = kmalloc(sizeof(*w), GFP_ATOMIC); if (!w) return; - INIT_WORK(&w->work, __random_once_deferred); + INIT_WORK(&w->work, once_deferred); w->key = key; schedule_work(&w->work); } -bool __get_random_once(void *buf, int nbytes, bool *done, - struct static_key *once_key) -{ - static DEFINE_SPINLOCK(lock); - unsigned long flags; +static DEFINE_SPINLOCK(once_lock); - spin_lock_irqsave(&lock, flags); +bool __do_once_start(bool *done, unsigned long *flags) + __acquires(once_lock) +{ + spin_lock_irqsave(&once_lock, *flags); if (*done) { - spin_unlock_irqrestore(&lock, flags); + spin_unlock_irqrestore(&once_lock, *flags); + /* Keep sparse happy by restoring an even lock count on + * this lock. In case we return here, we don't call into + * __do_once_done but return early in the DO_ONCE() macro. + */ + __acquire(once_lock); return false; } - get_random_bytes(buf, nbytes); - *done = true; - spin_unlock_irqrestore(&lock, flags); - - __random_once_disable_jump(once_key); - return true; } -EXPORT_SYMBOL(__get_random_once); +EXPORT_SYMBOL(__do_once_start); + +void __do_once_done(bool *done, struct static_key *once_key, + unsigned long *flags) + __releases(once_lock) +{ + *done = true; + spin_unlock_irqrestore(&once_lock, *flags); + once_disable_jump(once_key); +} +EXPORT_SYMBOL(__do_once_done); -- cgit v0.10.2 From 0dd50d1b0c003ab4f17597fe1198bb57a2fadc06 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 8 Oct 2015 01:20:37 +0200 Subject: random32: add prandom_seed_full_state helper Factor out the full reseed handling code that populates the state through get_random_bytes() and runs prandom_warmup(). The resulting prandom_seed_full_state() will be used later on in more than the current __prandom_reseed() user. Fix also two minor whitespace issues along the way. Signed-off-by: Daniel Borkmann Acked-by: Hannes Frederic Sowa Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller diff --git a/lib/random32.c b/lib/random32.c index 0bee183..36c09fb 100644 --- a/lib/random32.c +++ b/lib/random32.c @@ -181,7 +181,7 @@ void prandom_seed(u32 entropy) * No locking on the CPUs, but then somewhat random results are, well, * expected. */ - for_each_possible_cpu (i) { + for_each_possible_cpu(i) { struct rnd_state *state = &per_cpu(net_rand_state, i); state->s1 = __seed(state->s1 ^ entropy, 2U); @@ -201,7 +201,7 @@ static int __init prandom_init(void) prandom_state_selftest(); for_each_possible_cpu(i) { - struct rnd_state *state = &per_cpu(net_rand_state,i); + struct rnd_state *state = &per_cpu(net_rand_state, i); u32 weak_seed = (i + jiffies) ^ random_get_entropy(); prandom_seed_early(state, weak_seed, true); @@ -238,13 +238,30 @@ static void __init __prandom_start_seed_timer(void) add_timer(&seed_timer); } +static void prandom_seed_full_state(struct rnd_state __percpu *pcpu_state) +{ + int i; + + for_each_possible_cpu(i) { + struct rnd_state *state = per_cpu_ptr(pcpu_state, i); + u32 seeds[4]; + + get_random_bytes(&seeds, sizeof(seeds)); + state->s1 = __seed(seeds[0], 2U); + state->s2 = __seed(seeds[1], 8U); + state->s3 = __seed(seeds[2], 16U); + state->s4 = __seed(seeds[3], 128U); + + prandom_warmup(state); + } +} + /* * Generate better values after random number generator * is fully initialized. */ static void __prandom_reseed(bool late) { - int i; unsigned long flags; static bool latch = false; static DEFINE_SPINLOCK(lock); @@ -266,19 +283,7 @@ static void __prandom_reseed(bool late) goto out; latch = true; - - for_each_possible_cpu(i) { - struct rnd_state *state = &per_cpu(net_rand_state,i); - u32 seeds[4]; - - get_random_bytes(&seeds, sizeof(seeds)); - state->s1 = __seed(seeds[0], 2U); - state->s2 = __seed(seeds[1], 8U); - state->s3 = __seed(seeds[2], 16U); - state->s4 = __seed(seeds[3], 128U); - - prandom_warmup(state); - } + prandom_seed_full_state(&net_rand_state); out: spin_unlock_irqrestore(&lock, flags); } -- cgit v0.10.2 From 897ece56e714a2cc64e6914cb89a362d7021b36e Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 8 Oct 2015 01:20:38 +0200 Subject: random32: add prandom_init_once helper for own rngs Add a prandom_init_once() facility that works on the rnd_state, so that users that are keeping their own state independent from prandom_u32() can initialize their taus113 per cpu states. The motivation here is similar to net_get_random_once(): initialize the state as late as possible in the hope that enough entropy has been collected for the seeding. prandom_init_once() makes use of the recently introduced prandom_seed_full_state() helper and is generic enough so that it could also be used on fast-paths due to the DO_ONCE(). Signed-off-by: Daniel Borkmann Acked-by: Hannes Frederic Sowa Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller diff --git a/include/linux/random.h b/include/linux/random.h index e651874..a75840c 100644 --- a/include/linux/random.h +++ b/include/linux/random.h @@ -7,6 +7,8 @@ #define _LINUX_RANDOM_H #include +#include + #include struct random_ready_callback { @@ -45,6 +47,10 @@ struct rnd_state { u32 prandom_u32_state(struct rnd_state *state); void prandom_bytes_state(struct rnd_state *state, void *buf, size_t nbytes); +void prandom_seed_full_state(struct rnd_state __percpu *pcpu_state); + +#define prandom_init_once(pcpu_state) \ + DO_ONCE(prandom_seed_full_state, (pcpu_state)) /** * prandom_u32_max - returns a pseudo-random number in interval [0, ep_ro) diff --git a/lib/random32.c b/lib/random32.c index 36c09fb..1211191 100644 --- a/lib/random32.c +++ b/lib/random32.c @@ -238,7 +238,7 @@ static void __init __prandom_start_seed_timer(void) add_timer(&seed_timer); } -static void prandom_seed_full_state(struct rnd_state __percpu *pcpu_state) +void prandom_seed_full_state(struct rnd_state __percpu *pcpu_state) { int i; -- cgit v0.10.2 From 3ad0040573b0c00f88488bc31958acd07a55ee2e Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 8 Oct 2015 01:20:39 +0200 Subject: bpf: split state from prandom_u32() and consolidate {c, e}BPF prngs While recently arguing on a seccomp discussion that raw prandom_u32() access shouldn't be exposed to unpriviledged user space, I forgot the fact that SKF_AD_RANDOM extension actually already does it for some time in cBPF via commit 4cd3675ebf74 ("filter: added BPF random opcode"). Since prandom_u32() is being used in a lot of critical networking code, lets be more conservative and split their states. Furthermore, consolidate eBPF and cBPF prandom handlers to use the new internal PRNG. For eBPF, bpf_get_prandom_u32() was only accessible for priviledged users, but should that change one day, we also don't want to leak raw sequences through things like eBPF maps. One thought was also to have own per bpf_prog states, but due to ABI reasons this is not easily possible, i.e. the program code currently cannot access bpf_prog itself, and copying the rnd_state to/from the stack scratch space whenever a program uses the prng seems not really worth the trouble and seems too hacky. If needed, taus113 could in such cases be implemented within eBPF using a map entry to keep the state space, or get_random_bytes() could become a second helper in cases where performance would not be critical. Both sides can trigger a one-time late init via prandom_init_once() on the shared state. Performance-wise, there should even be a tiny gain as bpf_user_rnd_u32() saves one function call. The PRNG needs to live inside the BPF core since kernels could have a NET-less config as well. Signed-off-by: Daniel Borkmann Acked-by: Hannes Frederic Sowa Acked-by: Alexei Starovoitov Cc: Chema Gonzalez Signed-off-by: David S. Miller diff --git a/include/linux/bpf.h b/include/linux/bpf.h index c915a6b..3697ad5 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -200,4 +200,8 @@ extern const struct bpf_func_proto bpf_get_current_comm_proto; extern const struct bpf_func_proto bpf_skb_vlan_push_proto; extern const struct bpf_func_proto bpf_skb_vlan_pop_proto; +/* Shared helpers among cBPF and eBPF. */ +void bpf_user_rnd_init_once(void); +u64 bpf_user_rnd_u32(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5); + #endif /* _LINUX_BPF_H */ diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index c8855c2..8086471 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -731,6 +731,32 @@ void bpf_prog_free(struct bpf_prog *fp) } EXPORT_SYMBOL_GPL(bpf_prog_free); +/* RNG for unpriviledged user space with separated state from prandom_u32(). */ +static DEFINE_PER_CPU(struct rnd_state, bpf_user_rnd_state); + +void bpf_user_rnd_init_once(void) +{ + prandom_init_once(&bpf_user_rnd_state); +} + +u64 bpf_user_rnd_u32(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5) +{ + /* Should someone ever have the rather unwise idea to use some + * of the registers passed into this function, then note that + * this function is called from native eBPF and classic-to-eBPF + * transformations. Register assignments from both sides are + * different, f.e. classic always sets fn(ctx, A, X) here. + */ + struct rnd_state *state; + u32 res; + + state = &get_cpu_var(bpf_user_rnd_state); + res = prandom_u32_state(state); + put_cpu_var(state); + + return res; +} + /* Weak definitions of helper functions in case we don't have bpf syscall. */ const struct bpf_func_proto bpf_map_lookup_elem_proto __weak; const struct bpf_func_proto bpf_map_update_elem_proto __weak; diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 1447ec0..4504ca6 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -93,13 +93,8 @@ const struct bpf_func_proto bpf_map_delete_elem_proto = { .arg2_type = ARG_PTR_TO_MAP_KEY, }; -static u64 bpf_get_prandom_u32(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5) -{ - return prandom_u32(); -} - const struct bpf_func_proto bpf_get_prandom_u32_proto = { - .func = bpf_get_prandom_u32, + .func = bpf_user_rnd_u32, .gpl_only = false, .ret_type = RET_INTEGER, }; diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 5f35f42..c868caf 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -404,6 +404,8 @@ static void fixup_bpf_calls(struct bpf_prog *prog) if (insn->imm == BPF_FUNC_get_route_realm) prog->dst_needed = 1; + if (insn->imm == BPF_FUNC_get_prandom_u32) + bpf_user_rnd_init_once(); if (insn->imm == BPF_FUNC_tail_call) { /* mark bpf_tail_call as different opcode * to avoid conditional branch in diff --git a/net/core/filter.c b/net/core/filter.c index 8f4603c..342e6c8 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -149,12 +149,6 @@ static u64 __get_raw_cpu_id(u64 ctx, u64 a, u64 x, u64 r4, u64 r5) return raw_smp_processor_id(); } -/* note that this only generates 32-bit random numbers */ -static u64 __get_random_u32(u64 ctx, u64 a, u64 x, u64 r4, u64 r5) -{ - return prandom_u32(); -} - static u32 convert_skb_access(int skb_field, int dst_reg, int src_reg, struct bpf_insn *insn_buf) { @@ -313,7 +307,8 @@ static bool convert_bpf_extensions(struct sock_filter *fp, *insn = BPF_EMIT_CALL(__get_raw_cpu_id); break; case SKF_AD_OFF + SKF_AD_RANDOM: - *insn = BPF_EMIT_CALL(__get_random_u32); + *insn = BPF_EMIT_CALL(bpf_user_rnd_u32); + bpf_user_rnd_init_once(); break; } break; -- cgit v0.10.2 From f640ee98bbeaa169684a571e0b96bea563bb6015 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 8 Oct 2015 12:35:42 +0200 Subject: Bluetooth: Fix basic debugfs entries for unconfigured controllers When the controller is unconfigured (for example it does not have a valid Bluetooth address), then the basic debugfs entries for dut_mode and vendor_diag are not creates. Ensure they are created in __hci_init and also __hci_unconf_init functions. One of them is called during setup stage of a new controller. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index b2095ca..d2b3dd3 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -184,6 +184,16 @@ static const struct file_operations vendor_diag_fops = { .llseek = default_llseek, }; +static void hci_debugfs_create_basic(struct hci_dev *hdev) +{ + debugfs_create_file("dut_mode", 0644, hdev->debugfs, hdev, + &dut_mode_fops); + + if (hdev->set_diag) + debugfs_create_file("vendor_diag", 0644, hdev->debugfs, hdev, + &vendor_diag_fops); +} + /* ---- HCI requests ---- */ static void hci_req_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode, @@ -900,20 +910,8 @@ static int __hci_init(struct hci_dev *hdev) if (err < 0) return err; - if (hci_dev_test_flag(hdev, HCI_SETUP)) { - /* The Device Under Test (DUT) mode is special and available - * for all controller types. So just create it early on. - */ - debugfs_create_file("dut_mode", 0644, hdev->debugfs, hdev, - &dut_mode_fops); - - /* When the driver supports the set_diag callback, then - * expose an entry to modify the vendor diagnostic setting. - */ - if (hdev->set_diag) - debugfs_create_file("vendor_diag", 0644, hdev->debugfs, - hdev, &vendor_diag_fops); - } + if (hci_dev_test_flag(hdev, HCI_SETUP)) + hci_debugfs_create_basic(hdev); err = __hci_req_sync(hdev, hci_init2_req, 0, HCI_INIT_TIMEOUT); if (err < 0) @@ -990,6 +988,9 @@ static int __hci_unconf_init(struct hci_dev *hdev) if (err < 0) return err; + if (hci_dev_test_flag(hdev, HCI_SETUP)) + hci_debugfs_create_basic(hdev); + return 0; } -- cgit v0.10.2 From 1e8efb42cf32982d68c1ceab3e3dc15a05157632 Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Thu, 27 Aug 2015 11:42:33 -0400 Subject: i40e: fix erroneous WARN_ON The driver was issuing a WARN_ON during ring size changes because the code was cloning the rx_ring struct but not zeroing out the pointers before allocating new memory. Zero out the pointers in the cloned copy before allocating new memory for them. In this case the code was correctly avoiding memory leaks but still triggering the warning. Change-ID: I186dd493948e9b7254ab0593d4aad8b68808918d Signed-off-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index ffa9431..ef471fc 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -1176,6 +1176,11 @@ static int i40e_set_ringparam(struct net_device *netdev, /* clone ring and setup updated count */ tx_rings[i] = *vsi->tx_rings[i]; tx_rings[i].count = new_tx_count; + /* the desc and bi pointers will be reallocated in the + * setup call + */ + tx_rings[i].desc = NULL; + tx_rings[i].rx_bi = NULL; err = i40e_setup_tx_descriptors(&tx_rings[i]); if (err) { while (i) { @@ -1206,6 +1211,11 @@ static int i40e_set_ringparam(struct net_device *netdev, /* clone ring and setup updated count */ rx_rings[i] = *vsi->rx_rings[i]; rx_rings[i].count = new_rx_count; + /* the desc and bi pointers will be reallocated in the + * setup call + */ + rx_rings[i].desc = NULL; + rx_rings[i].rx_bi = NULL; err = i40e_setup_rx_descriptors(&rx_rings[i]); if (err) { while (i) { -- cgit v0.10.2 From 02d109be3ddc9768c1c38709218648d0c48a4ea9 Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Thu, 27 Aug 2015 11:42:34 -0400 Subject: i40e: inline interrupt enable The interrupt enable function can be inlined by moving it to the header file, which decreases the function call overhead for a frequently called function. Change-ID: I3214cc99593725768642680e7b8ce7e9bba7e44d Signed-off-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 681bd5d..7a3c939 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -702,7 +702,24 @@ static inline void i40e_dbg_pf_exit(struct i40e_pf *pf) {} static inline void i40e_dbg_init(void) {} static inline void i40e_dbg_exit(void) {} #endif /* CONFIG_DEBUG_FS*/ -void i40e_irq_dynamic_enable(struct i40e_vsi *vsi, int vector); +/** + * i40e_irq_dynamic_enable - Enable default interrupt generation settings + * @vsi: pointer to a vsi + * @vector: enable a particular Hw Interrupt vector, without base_vector + **/ +static inline void i40e_irq_dynamic_enable(struct i40e_vsi *vsi, int vector) +{ + struct i40e_pf *pf = vsi->back; + struct i40e_hw *hw = &pf->hw; + u32 val; + + val = I40E_PFINT_DYN_CTLN_INTENA_MASK | + I40E_PFINT_DYN_CTLN_CLEARPBA_MASK | + (I40E_ITR_NONE << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT); + wr32(hw, I40E_PFINT_DYN_CTLN(vector + vsi->base_vector - 1), val); + /* skip the flush */ +} + void i40e_irq_dynamic_disable(struct i40e_vsi *vsi, int vector); void i40e_irq_dynamic_disable_icr0(struct i40e_pf *pf); void i40e_irq_dynamic_enable_icr0(struct i40e_pf *pf); diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index fb4b34d..2c59214 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -3060,24 +3060,6 @@ void i40e_irq_dynamic_enable_icr0(struct i40e_pf *pf) } /** - * i40e_irq_dynamic_enable - Enable default interrupt generation settings - * @vsi: pointer to a vsi - * @vector: enable a particular Hw Interrupt vector, without base_vector - **/ -void i40e_irq_dynamic_enable(struct i40e_vsi *vsi, int vector) -{ - struct i40e_pf *pf = vsi->back; - struct i40e_hw *hw = &pf->hw; - u32 val; - - val = I40E_PFINT_DYN_CTLN_INTENA_MASK | - I40E_PFINT_DYN_CTLN_CLEARPBA_MASK | - (I40E_ITR_NONE << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT); - wr32(hw, I40E_PFINT_DYN_CTLN(vector + vsi->base_vector - 1), val); - /* skip the flush */ -} - -/** * i40e_irq_dynamic_disable - Disable default interrupt generation settings * @vsi: pointer to a vsi * @vector: disable a particular Hw Interrupt vector -- cgit v0.10.2 From 96c8d0738aff3ecf3e8b3253fb286685e35d93e6 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Thu, 27 Aug 2015 11:42:35 -0400 Subject: i40e: add more verbose error messages Under certain circumstances, the device may not have enough resources to enable all of the VFs that it advertises in config space. Although the number of supported VFs is reported upon driver init, it is not obvious when this is different from the number reported in config space. To eliminate this confusion, add an error message explaining the problem. Additionally, move the 'Allocating VFs' message down below the error checks so as to prevent further confusion. Change-ID: I45b7efca53a7aebf7777be33a8bc9d615ae48ea1 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 0545e3f..fac8a02 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -998,17 +998,19 @@ static int i40e_pci_sriov_enable(struct pci_dev *pdev, int num_vfs) goto err_out; } - dev_info(&pdev->dev, "Allocating %d VFs.\n", num_vfs); if (pre_existing_vfs && pre_existing_vfs != num_vfs) i40e_free_vfs(pf); else if (pre_existing_vfs && pre_existing_vfs == num_vfs) goto out; if (num_vfs > pf->num_req_vfs) { + dev_warn(&pdev->dev, "Unable to enable %d VFs. Limited to %d VFs due to device resource constraints.\n", + num_vfs, pf->num_req_vfs); err = -EPERM; goto err_out; } + dev_info(&pdev->dev, "Allocating %d VFs.\n", num_vfs); err = i40e_alloc_vfs(pf, num_vfs); if (err) { dev_warn(&pdev->dev, "Failed to enable SR-IOV: %d\n", err); -- cgit v0.10.2 From 909b2d16c41f3a56ff67342073ae5f2569d58084 Mon Sep 17 00:00:00 2001 From: Neerav Parikh Date: Thu, 27 Aug 2015 11:42:36 -0400 Subject: i40e: Add parsing for CEE DCBX TLVs This patch adds parsing for CEE DCBX TLVs from the LLDP MIB. While the driver gets the DCB CEE operational configuration from Firmware using the "Get CEE DCBX Oper Config" AQ command there is a need to get the CEE DesiredCfg Tx by firmware and DCB configuration Rx from peer; for debug and other application purposes. Change-ID: I9140edf1a25a2852c7eff805d81e5eff6266178d Signed-off-by: Neerav Parikh Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb.c b/drivers/net/ethernet/intel/i40e/i40e_dcb.c index 9aee35d..89e60e3 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb.c +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb.c @@ -292,6 +292,182 @@ static void i40e_parse_ieee_tlv(struct i40e_lldp_org_tlv *tlv, } /** + * i40e_parse_cee_pgcfg_tlv + * @tlv: CEE DCBX PG CFG TLV + * @dcbcfg: Local store to update ETS CFG data + * + * Parses CEE DCBX PG CFG TLV + **/ +static void i40e_parse_cee_pgcfg_tlv(struct i40e_cee_feat_tlv *tlv, + struct i40e_dcbx_config *dcbcfg) +{ + struct i40e_dcb_ets_config *etscfg; + u8 *buf = tlv->tlvinfo; + u16 offset = 0; + u8 priority; + int i; + + etscfg = &dcbcfg->etscfg; + + if (tlv->en_will_err & I40E_CEE_FEAT_TLV_WILLING_MASK) + etscfg->willing = 1; + + etscfg->cbs = 0; + /* Priority Group Table (4 octets) + * Octets:| 1 | 2 | 3 | 4 | + * ----------------------------------------- + * |pri0|pri1|pri2|pri3|pri4|pri5|pri6|pri7| + * ----------------------------------------- + * Bits:|7 4|3 0|7 4|3 0|7 4|3 0|7 4|3 0| + * ----------------------------------------- + */ + for (i = 0; i < 4; i++) { + priority = (u8)((buf[offset] & I40E_CEE_PGID_PRIO_1_MASK) >> + I40E_CEE_PGID_PRIO_1_SHIFT); + etscfg->prioritytable[i * 2] = priority; + priority = (u8)((buf[offset] & I40E_CEE_PGID_PRIO_0_MASK) >> + I40E_CEE_PGID_PRIO_0_SHIFT); + etscfg->prioritytable[i * 2 + 1] = priority; + offset++; + } + + /* PG Percentage Table (8 octets) + * Octets:| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | + * --------------------------------- + * |pg0|pg1|pg2|pg3|pg4|pg5|pg6|pg7| + * --------------------------------- + */ + for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) + etscfg->tcbwtable[i] = buf[offset++]; + + /* Number of TCs supported (1 octet) */ + etscfg->maxtcs = buf[offset]; +} + +/** + * i40e_parse_cee_pfccfg_tlv + * @tlv: CEE DCBX PFC CFG TLV + * @dcbcfg: Local store to update PFC CFG data + * + * Parses CEE DCBX PFC CFG TLV + **/ +static void i40e_parse_cee_pfccfg_tlv(struct i40e_cee_feat_tlv *tlv, + struct i40e_dcbx_config *dcbcfg) +{ + u8 *buf = tlv->tlvinfo; + + if (tlv->en_will_err & I40E_CEE_FEAT_TLV_WILLING_MASK) + dcbcfg->pfc.willing = 1; + + /* ------------------------ + * | PFC Enable | PFC TCs | + * ------------------------ + * | 1 octet | 1 octet | + */ + dcbcfg->pfc.pfcenable = buf[0]; + dcbcfg->pfc.pfccap = buf[1]; +} + +/** + * i40e_parse_cee_app_tlv + * @tlv: CEE DCBX APP TLV + * @dcbcfg: Local store to update APP PRIO data + * + * Parses CEE DCBX APP PRIO TLV + **/ +static void i40e_parse_cee_app_tlv(struct i40e_cee_feat_tlv *tlv, + struct i40e_dcbx_config *dcbcfg) +{ + u16 length, typelength, offset = 0; + struct i40e_cee_app_prio *app; + u8 i, up; + + typelength = ntohs(tlv->hdr.typelen); + length = (u16)((typelength & I40E_LLDP_TLV_LEN_MASK) >> + I40E_LLDP_TLV_LEN_SHIFT); + + dcbcfg->numapps = length / sizeof(*app); + if (!dcbcfg->numapps) + return; + + for (i = 0; i < dcbcfg->numapps; i++) { + app = (struct i40e_cee_app_prio *)(tlv->tlvinfo + offset); + for (up = 0; up < I40E_MAX_USER_PRIORITY; up++) { + if (app->prio_map & (1 << up)) + break; + } + dcbcfg->app[i].priority = up; + /* Get Selector from lower 2 bits */ + dcbcfg->app[i].selector = (app->upper_oui_sel & + I40E_CEE_APP_SELECTOR_MASK); + dcbcfg->app[i].protocolid = ntohs(app->protocol); + /* Move to next app */ + offset += sizeof(*app); + } +} + +/** + * i40e_parse_cee_tlv + * @tlv: CEE DCBX TLV + * @dcbcfg: Local store to update DCBX config data + * + * Get the TLV subtype and send it to parsing function + * based on the subtype value + **/ +static void i40e_parse_cee_tlv(struct i40e_lldp_org_tlv *tlv, + struct i40e_dcbx_config *dcbcfg) +{ + u16 len, tlvlen, sublen, typelength; + struct i40e_cee_feat_tlv *sub_tlv; + u8 subtype, feat_tlv_count = 0; + u32 ouisubtype; + + ouisubtype = ntohl(tlv->ouisubtype); + subtype = (u8)((ouisubtype & I40E_LLDP_TLV_SUBTYPE_MASK) >> + I40E_LLDP_TLV_SUBTYPE_SHIFT); + /* Return if not CEE DCBX */ + if (subtype != I40E_CEE_DCBX_TYPE) + return; + + typelength = ntohs(tlv->typelength); + tlvlen = (u16)((typelength & I40E_LLDP_TLV_LEN_MASK) >> + I40E_LLDP_TLV_LEN_SHIFT); + len = sizeof(tlv->typelength) + sizeof(ouisubtype) + + sizeof(struct i40e_cee_ctrl_tlv); + /* Return if no CEE DCBX Feature TLVs */ + if (tlvlen <= len) + return; + + sub_tlv = (struct i40e_cee_feat_tlv *)((char *)tlv + len); + while (feat_tlv_count < I40E_CEE_MAX_FEAT_TYPE) { + typelength = ntohs(sub_tlv->hdr.typelen); + sublen = (u16)((typelength & + I40E_LLDP_TLV_LEN_MASK) >> + I40E_LLDP_TLV_LEN_SHIFT); + subtype = (u8)((typelength & I40E_LLDP_TLV_TYPE_MASK) >> + I40E_LLDP_TLV_TYPE_SHIFT); + switch (subtype) { + case I40E_CEE_SUBTYPE_PG_CFG: + i40e_parse_cee_pgcfg_tlv(sub_tlv, dcbcfg); + break; + case I40E_CEE_SUBTYPE_PFC_CFG: + i40e_parse_cee_pfccfg_tlv(sub_tlv, dcbcfg); + break; + case I40E_CEE_SUBTYPE_APP_PRI: + i40e_parse_cee_app_tlv(sub_tlv, dcbcfg); + break; + default: + return; /* Invalid Sub-type return */ + } + feat_tlv_count++; + /* Move to next sub TLV */ + sub_tlv = (struct i40e_cee_feat_tlv *)((char *)sub_tlv + + sizeof(sub_tlv->hdr.typelen) + + sublen); + } +} + +/** * i40e_parse_org_tlv * @tlv: Organization specific TLV * @dcbcfg: Local store to update ETS REC data @@ -312,6 +488,9 @@ static void i40e_parse_org_tlv(struct i40e_lldp_org_tlv *tlv, case I40E_IEEE_8021QAZ_OUI: i40e_parse_ieee_tlv(tlv, dcbcfg); break; + case I40E_CEE_DCBX_OUI: + i40e_parse_cee_tlv(tlv, dcbcfg); + break; default: break; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb.h b/drivers/net/ethernet/intel/i40e/i40e_dcb.h index 50fc894..92d0104 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb.h +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb.h @@ -44,6 +44,15 @@ #define I40E_IEEE_SUBTYPE_PFC_CFG 11 #define I40E_IEEE_SUBTYPE_APP_PRI 12 +#define I40E_CEE_DCBX_OUI 0x001b21 +#define I40E_CEE_DCBX_TYPE 2 + +#define I40E_CEE_SUBTYPE_CTRL 1 +#define I40E_CEE_SUBTYPE_PG_CFG 2 +#define I40E_CEE_SUBTYPE_PFC_CFG 3 +#define I40E_CEE_SUBTYPE_APP_PRI 4 + +#define I40E_CEE_MAX_FEAT_TYPE 3 /* Defines for LLDP TLV header */ #define I40E_LLDP_TLV_LEN_SHIFT 0 #define I40E_LLDP_TLV_LEN_MASK (0x01FF << I40E_LLDP_TLV_LEN_SHIFT) @@ -98,6 +107,36 @@ struct i40e_lldp_org_tlv { __be32 ouisubtype; u8 tlvinfo[1]; }; + +struct i40e_cee_tlv_hdr { + __be16 typelen; + u8 operver; + u8 maxver; +}; + +struct i40e_cee_ctrl_tlv { + struct i40e_cee_tlv_hdr hdr; + __be32 seqno; + __be32 ackno; +}; + +struct i40e_cee_feat_tlv { + struct i40e_cee_tlv_hdr hdr; + u8 en_will_err; /* Bits: |En|Will|Err|Reserved(5)| */ +#define I40E_CEE_FEAT_TLV_ENABLE_MASK 0x80 +#define I40E_CEE_FEAT_TLV_WILLING_MASK 0x40 +#define I40E_CEE_FEAT_TLV_ERR_MASK 0x20 + u8 subtype; + u8 tlvinfo[1]; +}; + +struct i40e_cee_app_prio { + __be16 protocol; + u8 upper_oui_sel; /* Bits: |Upper OUI(6)|Selector(2)| */ +#define I40E_CEE_APP_SELECTOR_MASK 0x03 + __be16 lower_oui; + u8 prio_map; +}; #pragma pack() i40e_status i40e_get_dcbx_status(struct i40e_hw *hw, -- cgit v0.10.2 From 1a9375eb7f987e9338c17504dd2b358b688b32d8 Mon Sep 17 00:00:00 2001 From: Neerav Parikh Date: Thu, 27 Aug 2015 11:42:37 -0400 Subject: i40e/i40evf: Store CEE DCBX DesiredCfg and RemoteCfg This patch adds capability to query and store the CEE DCBX DesiredCfg and RemoteCfg data from the LLDP MIB. Added new member "desired_dcbx_config" in the i40e_hw data structure to hold CEE only DesiredCfg data. Change-ID: I19c550369594384eaff4cc63e690ca740231195d Signed-off-by: Neerav Parikh Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb.c b/drivers/net/ethernet/intel/i40e/i40e_dcb.c index 89e60e3..fbec7d7 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb.c +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb.c @@ -762,6 +762,36 @@ static void i40e_cee_to_dcb_config( } /** + * i40e_get_ieee_dcb_config + * @hw: pointer to the hw struct + * + * Get IEEE mode DCB configuration from the Firmware + **/ +static i40e_status i40e_get_ieee_dcb_config(struct i40e_hw *hw) +{ + i40e_status ret = 0; + + /* IEEE mode */ + hw->local_dcbx_config.dcbx_mode = I40E_DCBX_MODE_IEEE; + /* Get Local DCB Config */ + ret = i40e_aq_get_dcb_config(hw, I40E_AQ_LLDP_MIB_LOCAL, 0, + &hw->local_dcbx_config); + if (ret) + goto out; + + /* Get Remote DCB Config */ + ret = i40e_aq_get_dcb_config(hw, I40E_AQ_LLDP_MIB_REMOTE, + I40E_AQ_LLDP_BRIDGE_TYPE_NEAREST_BRIDGE, + &hw->remote_dcbx_config); + /* Don't treat ENOENT as an error for Remote MIBs */ + if (hw->aq.asq_last_status == I40E_AQ_RC_ENOENT) + ret = 0; + +out: + return ret; +} + +/** * i40e_get_dcb_config * @hw: pointer to the hw struct * @@ -776,7 +806,7 @@ i40e_status i40e_get_dcb_config(struct i40e_hw *hw) /* If Firmware version < v4.33 IEEE only */ if (((hw->aq.fw_maj_ver == 4) && (hw->aq.fw_min_ver < 33)) || (hw->aq.fw_maj_ver < 4)) - goto ieee; + return i40e_get_ieee_dcb_config(hw); /* If Firmware version == v4.33 use old CEE struct */ if ((hw->aq.fw_maj_ver == 4) && (hw->aq.fw_min_ver == 33)) { @@ -805,16 +835,14 @@ i40e_status i40e_get_dcb_config(struct i40e_hw *hw) /* CEE mode not enabled try querying IEEE data */ if (hw->aq.asq_last_status == I40E_AQ_RC_ENOENT) - goto ieee; - else + return i40e_get_ieee_dcb_config(hw); + + if (ret) goto out; -ieee: - /* IEEE mode */ - hw->local_dcbx_config.dcbx_mode = I40E_DCBX_MODE_IEEE; - /* Get Local DCB Config */ + /* Get CEE DCB Desired Config */ ret = i40e_aq_get_dcb_config(hw, I40E_AQ_LLDP_MIB_LOCAL, 0, - &hw->local_dcbx_config); + &hw->desired_dcbx_config); if (ret) goto out; diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index 34720e0..1c0bedb 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -510,8 +510,9 @@ struct i40e_hw { u16 dcbx_status; /* DCBX info */ - struct i40e_dcbx_config local_dcbx_config; - struct i40e_dcbx_config remote_dcbx_config; + struct i40e_dcbx_config local_dcbx_config; /* Oper/Local Cfg */ + struct i40e_dcbx_config remote_dcbx_config; /* Peer Cfg */ + struct i40e_dcbx_config desired_dcbx_config; /* CEE Desired Cfg */ /* debug mask */ u32 debug_mask; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_type.h b/drivers/net/ethernet/intel/i40evf/i40e_type.h index bbb3886..4b5528d 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_type.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_type.h @@ -504,8 +504,9 @@ struct i40e_hw { u16 dcbx_status; /* DCBX info */ - struct i40e_dcbx_config local_dcbx_config; - struct i40e_dcbx_config remote_dcbx_config; + struct i40e_dcbx_config local_dcbx_config; /* Oper/Local Cfg */ + struct i40e_dcbx_config remote_dcbx_config; /* Peer Cfg */ + struct i40e_dcbx_config desired_dcbx_config; /* CEE Desired Cfg */ /* debug mask */ u32 debug_mask; -- cgit v0.10.2 From 3487b6c30c0dd2204dc31e14330097dff02d9a58 Mon Sep 17 00:00:00 2001 From: Carolyn Wyborny Date: Thu, 27 Aug 2015 11:42:38 -0400 Subject: i40e: Fix for extra Flow Director filter in table after error This patch fixes a problem where the PF's fdir filter table would have an entry that the hw was unable to add. This notification happens in the hot path, so instead of trying to fix it then, we note the location in the failure case and delete it during regular fdir subtask callback. Without this patch, a case can occur where an invalid entry gets replayed and a valid one is not. Change-ID: I67831c183b5d0309876de807cc434809b74c9cb7 Signed-off-by: Carolyn Wyborny Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 7a3c939..a662e39 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -410,6 +410,7 @@ struct i40e_pf { u32 npar_min_bw; u32 ioremap_len; + u32 fd_inv; }; struct i40e_mac_filter { diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 2c59214..94953568 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -5614,7 +5614,9 @@ u32 i40e_get_global_fd_count(struct i40e_pf *pf) **/ void i40e_fdir_check_and_reenable(struct i40e_pf *pf) { + struct i40e_fdir_filter *filter; u32 fcnt_prog, fcnt_avail; + struct hlist_node *node; if (test_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state)) return; @@ -5643,6 +5645,18 @@ void i40e_fdir_check_and_reenable(struct i40e_pf *pf) dev_info(&pf->pdev->dev, "ATR is being enabled since we have space in the table now\n"); } } + + /* if hw had a problem adding a filter, delete it */ + if (pf->fd_inv > 0) { + hlist_for_each_entry_safe(filter, node, + &pf->fdir_filter_list, fdir_node) { + if (filter->fd_id == pf->fd_inv) { + hlist_del(&filter->fdir_node); + kfree(filter); + pf->fdir_pf_active_filters--; + } + } + } } #define I40E_MIN_FD_FLUSH_INTERVAL 10 diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 889ed10..8ab7ab1 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -465,10 +465,11 @@ static void i40e_fd_handle_status(struct i40e_ring *rx_ring, I40E_RX_PROG_STATUS_DESC_QW1_ERROR_SHIFT; if (error == BIT(I40E_RX_PROG_STATUS_DESC_FD_TBL_FULL_SHIFT)) { + pf->fd_inv = le32_to_cpu(rx_desc->wb.qword0.hi_dword.fd_id); if ((rx_desc->wb.qword0.hi_dword.fd_id != 0) || (I40E_DEBUG_FD & pf->hw.debug_mask)) dev_warn(&pdev->dev, "ntuple filter loc = %d, could not be added\n", - rx_desc->wb.qword0.hi_dword.fd_id); + pf->fd_inv); /* Check if the programming error is for ATR. * If so, auto disable ATR and set a state for -- cgit v0.10.2 From c156f856ad8ba4f71bcdb2c92ba1a9effaa29ac1 Mon Sep 17 00:00:00 2001 From: Matt Jared Date: Thu, 27 Aug 2015 11:42:39 -0400 Subject: i40e: Fix multiple link up messages This patch addresses an issue where multiple link up messages can be logged resulting from aq link status timing when link properties are changed (fc, speed, etc.); solved by using a single function to handle status printing and adding a mechanism to track whether link state (up or down) has actually changed. Change-ID: Ied6ed6e49dc397c77d992adc0bc9ed3767152b9d Signed-off-by: Matt Jared Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index a662e39..0c73404 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -537,6 +537,7 @@ struct i40e_vsi { u16 idx; /* index in pf->vsi[] */ u16 veb_idx; /* index of VEB parent */ struct kobject *kobj; /* sysfs object */ + bool current_isup; /* Sync 'link up' logging */ /* VSI specific handlers */ irqreturn_t (*irq_handler)(int irq, void *data); @@ -791,4 +792,5 @@ int i40e_is_vsi_uplink_mode_veb(struct i40e_vsi *vsi); i40e_status i40e_get_npar_bw_setting(struct i40e_pf *pf); i40e_status i40e_set_npar_bw_setting(struct i40e_pf *pf); i40e_status i40e_commit_npar_bw_setting(struct i40e_pf *pf); +void i40e_print_link_message(struct i40e_vsi *vsi, bool isup); #endif /* _I40E_H_ */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index ef471fc..f2b4f8b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -690,7 +690,7 @@ static int i40e_set_settings(struct net_device *netdev, /* Tell the OS link is going down, the link will go * back up when fw says it is ready asynchronously */ - netdev_info(netdev, "PHY settings change requested, NIC Link is going down.\n"); + i40e_print_link_message(vsi, false); netif_carrier_off(netdev); netif_tx_stop_all_queues(netdev); } @@ -834,7 +834,7 @@ static int i40e_set_pauseparam(struct net_device *netdev, /* Tell the OS link is going down, the link will go back up when fw * says it is ready asynchronously */ - netdev_info(netdev, "Flow control settings change requested, NIC Link is going down.\n"); + i40e_print_link_message(vsi, false); netif_carrier_off(netdev); netif_tx_stop_all_queues(netdev); diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 94953568..e05e6aa 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -4837,11 +4837,14 @@ out: * i40e_print_link_message - print link up or down * @vsi: the VSI for which link needs a message */ -static void i40e_print_link_message(struct i40e_vsi *vsi, bool isup) +void i40e_print_link_message(struct i40e_vsi *vsi, bool isup) { char speed[SPEED_SIZE] = "Unknown"; char fc[FC_SIZE] = "RX/TX"; + if (vsi->current_isup == isup) + return; + vsi->current_isup = isup; if (!isup) { netdev_info(vsi->netdev, "NIC Link is Down\n"); return; -- cgit v0.10.2 From 9ac77266379d070c6d140ad44f86a99936497eeb Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Thu, 27 Aug 2015 11:42:40 -0400 Subject: i40e: add switch for link polling There's been some need for controlling the periodic link polling for debugging link issues. This patch enables switching it off and on through an ethtool private flag. The link poll remains on by default, but can be turned off with ethtool --set-priv-flags p261p1 LinkPolling off and later turned back on with ethtool --set-priv-flags p261p1 LinkPolling on To check the current status, use ethtool --show-priv-flags p261p1 Change-ID: I32e4ab654ff3eec90a06cf144899971b82d71c40 Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 0c73404..f26dcb2 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -101,6 +101,7 @@ /* Ethtool Private Flags */ #define I40E_PRIV_FLAGS_NPAR_FLAG BIT(0) +#define I40E_PRIV_FLAGS_LINKPOLL_FLAG BIT(1) #define I40E_NVM_VERSION_LO_SHIFT 0 #define I40E_NVM_VERSION_LO_MASK (0xff << I40E_NVM_VERSION_LO_SHIFT) @@ -327,6 +328,7 @@ struct i40e_pf { #define I40E_FLAG_WB_ON_ITR_CAPABLE BIT_ULL(35) #define I40E_FLAG_VEB_STATS_ENABLED BIT_ULL(37) #define I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE BIT_ULL(38) +#define I40E_FLAG_LINK_POLLING_ENABLED BIT_ULL(39) #define I40E_FLAG_VEB_MODE_ENABLED BIT_ULL(40) /* tracks features that get auto disabled by errors */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index f2b4f8b..5a726f2 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -230,10 +230,10 @@ static const char i40e_gstrings_test[][ETH_GSTRING_LEN] = { static const char i40e_priv_flags_strings[][ETH_GSTRING_LEN] = { "NPAR", + "LinkPolling", }; -#define I40E_PRIV_FLAGS_STR_LEN \ - (sizeof(i40e_priv_flags_strings) / ETH_GSTRING_LEN) +#define I40E_PRIV_FLAGS_STR_LEN ARRAY_SIZE(i40e_priv_flags_strings) /** * i40e_partition_setting_complaint - generic complaint for MFP restriction @@ -2636,10 +2636,31 @@ static u32 i40e_get_priv_flags(struct net_device *dev) ret_flags |= pf->hw.func_caps.npar_enable ? I40E_PRIV_FLAGS_NPAR_FLAG : 0; + ret_flags |= pf->flags & I40E_FLAG_LINK_POLLING_ENABLED ? + I40E_PRIV_FLAGS_LINKPOLL_FLAG : 0; return ret_flags; } +/** + * i40e_set_priv_flags - set private flags + * @dev: network interface device structure + * @flags: bit flags to be set + **/ +static int i40e_set_priv_flags(struct net_device *dev, u32 flags) +{ + struct i40e_netdev_priv *np = netdev_priv(dev); + struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; + + if (flags & I40E_PRIV_FLAGS_LINKPOLL_FLAG) + pf->flags |= I40E_FLAG_LINK_POLLING_ENABLED; + else + pf->flags &= ~I40E_FLAG_LINK_POLLING_ENABLED; + + return 0; +} + static const struct ethtool_ops i40e_ethtool_ops = { .get_settings = i40e_get_settings, .set_settings = i40e_set_settings, @@ -2676,6 +2697,7 @@ static const struct ethtool_ops i40e_ethtool_ops = { .set_channels = i40e_set_channels, .get_ts_info = i40e_get_ts_info, .get_priv_flags = i40e_get_priv_flags, + .set_priv_flags = i40e_set_priv_flags, }; void i40e_set_ethtool_ops(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 e05e6aa..23a7b40 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -5892,7 +5892,8 @@ static void i40e_watchdog_subtask(struct i40e_pf *pf) return; pf->service_timer_previous = jiffies; - i40e_link_event(pf); + if (pf->flags & I40E_FLAG_LINK_POLLING_ENABLED) + i40e_link_event(pf); /* Update the stats for active netdevs so the network stack * can look at updated numbers whenever it cares to @@ -7908,6 +7909,7 @@ static int i40e_sw_init(struct i40e_pf *pf) /* Set default capability flags */ pf->flags = I40E_FLAG_RX_CSUM_ENABLED | I40E_FLAG_MSI_ENABLED | + I40E_FLAG_LINK_POLLING_ENABLED | I40E_FLAG_MSIX_ENABLED; if (iommu_present(&pci_bus_type)) -- cgit v0.10.2 From 66486cd71723b2b41b36759159b551230ab8763d Mon Sep 17 00:00:00 2001 From: Serey Kong Date: Thu, 27 Aug 2015 11:42:41 -0400 Subject: i40e/i40evf: Explicitly assign enum index for VSI type Ran into an issue where PF's VSI type list was different from VF's, which was resulted in different enum index. The VSI type list can be different depending on what build flag is used for PF and VF. The change is to explicitly assign enum index for each VSI type so that PF and VF always reference to the same VSI type event if the enum lists are different. Change-ID: I8c0e5fdb515f324f7964df863a458073cf467e57 Signed-off-by: Serey Kong Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index 1c0bedb..d1ec5a4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -160,14 +160,14 @@ enum i40e_set_fc_aq_failures { }; enum i40e_vsi_type { - I40E_VSI_MAIN = 0, - I40E_VSI_VMDQ1, - I40E_VSI_VMDQ2, - I40E_VSI_CTRL, - I40E_VSI_FCOE, - I40E_VSI_MIRROR, - I40E_VSI_SRIOV, - I40E_VSI_FDIR, + I40E_VSI_MAIN = 0, + I40E_VSI_VMDQ1 = 1, + I40E_VSI_VMDQ2 = 2, + I40E_VSI_CTRL = 3, + I40E_VSI_FCOE = 4, + I40E_VSI_MIRROR = 5, + I40E_VSI_SRIOV = 6, + I40E_VSI_FDIR = 7, I40E_VSI_TYPE_UNKNOWN }; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_type.h b/drivers/net/ethernet/intel/i40evf/i40e_type.h index 4b5528d..a59b60f 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_type.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_type.h @@ -160,14 +160,14 @@ enum i40e_set_fc_aq_failures { }; enum i40e_vsi_type { - I40E_VSI_MAIN = 0, - I40E_VSI_VMDQ1, - I40E_VSI_VMDQ2, - I40E_VSI_CTRL, - I40E_VSI_FCOE, - I40E_VSI_MIRROR, - I40E_VSI_SRIOV, - I40E_VSI_FDIR, + I40E_VSI_MAIN = 0, + I40E_VSI_VMDQ1 = 1, + I40E_VSI_VMDQ2 = 2, + I40E_VSI_CTRL = 3, + I40E_VSI_FCOE = 4, + I40E_VSI_MIRROR = 5, + I40E_VSI_SRIOV = 6, + I40E_VSI_FDIR = 7, I40E_VSI_TYPE_UNKNOWN }; -- cgit v0.10.2 From 95e5613f75ac93d0547b7d2077030a5fe11af21e Mon Sep 17 00:00:00 2001 From: Greg Bowers Date: Fri, 28 Aug 2015 17:55:52 -0400 Subject: i40e: Support FW CEE DCB UP to TC map nibble swap Changes parsing of AQ command Get CEE DCBX OPER CFG (0x0A07). Change is required because FW creates the oper_prio_tc nibbles reversed from those in the CEE Priority Group sub-TLV. Change-ID: I7d9d8641bb430d30e286fc3fac909866ef8a0de8 Signed-off-by: Greg Bowers Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb.c b/drivers/net/ethernet/intel/i40e/i40e_dcb.c index fbec7d7..6fa07ef 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb.c +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb.c @@ -681,15 +681,18 @@ static void i40e_cee_to_dcb_config( /* CEE PG data to ETS config */ dcbcfg->etscfg.maxtcs = cee_cfg->oper_num_tc; + /* Note that the FW creates the oper_prio_tc nibbles reversed + * from those in the CEE Priority Group sub-TLV. + */ for (i = 0; i < 4; i++) { tc = (u8)((cee_cfg->oper_prio_tc[i] & - I40E_CEE_PGID_PRIO_1_MASK) >> - I40E_CEE_PGID_PRIO_1_SHIFT); - dcbcfg->etscfg.prioritytable[i*2] = tc; - tc = (u8)((cee_cfg->oper_prio_tc[i] & I40E_CEE_PGID_PRIO_0_MASK) >> I40E_CEE_PGID_PRIO_0_SHIFT); - dcbcfg->etscfg.prioritytable[i*2 + 1] = tc; + dcbcfg->etscfg.prioritytable[i * 2] = tc; + tc = (u8)((cee_cfg->oper_prio_tc[i] & + I40E_CEE_PGID_PRIO_1_MASK) >> + I40E_CEE_PGID_PRIO_1_SHIFT); + dcbcfg->etscfg.prioritytable[i * 2 + 1] = tc; } for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) -- cgit v0.10.2 From ee5c1e92dd01d372b8e054b5a7e1cc19a1d32815 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Fri, 28 Aug 2015 17:55:53 -0400 Subject: i40evf: detect reset more reliably Using VFGEN_RSTAT to detect a VF reset is an endeavor that is fraught with peril. It's entirely too easy to miss a reset because none of the bits are sticky. By the time the VF driver reads the register, the reset may have been processed and cleaned up by the PF driver, leaving the register in the same state that it was before the reset. Instead, detect a reset with the VF_ARQLEN register. When the VF is reset, the enable bit in this register is cleared, and it stays cleared until the VF driver processes the reset and re-enables the admin queue. Because we now deal with multiple registers in the reset and watchdog tasks, rename the rstat_val variable to reg_val. Change-ID: Id1df17045c0992e607da0162d31807f7fc20d199 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 0d18446..664fde6 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -1419,16 +1419,16 @@ static void i40evf_watchdog_task(struct work_struct *work) struct i40evf_adapter, watchdog_task); struct i40e_hw *hw = &adapter->hw; - uint32_t rstat_val; + u32 reg_val; if (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section)) goto restart_watchdog; if (adapter->flags & I40EVF_FLAG_PF_COMMS_FAILED) { - rstat_val = rd32(hw, I40E_VFGEN_RSTAT) & - I40E_VFGEN_RSTAT_VFR_STATE_MASK; - if ((rstat_val == I40E_VFR_VFACTIVE) || - (rstat_val == I40E_VFR_COMPLETED)) { + reg_val = rd32(hw, I40E_VFGEN_RSTAT) & + I40E_VFGEN_RSTAT_VFR_STATE_MASK; + if ((reg_val == I40E_VFR_VFACTIVE) || + (reg_val == I40E_VFR_COMPLETED)) { /* A chance for redemption! */ dev_err(&adapter->pdev->dev, "Hardware came out of reset. Attempting reinit.\n"); adapter->state = __I40EVF_STARTUP; @@ -1453,11 +1453,8 @@ static void i40evf_watchdog_task(struct work_struct *work) goto watchdog_done; /* check for reset */ - rstat_val = rd32(hw, I40E_VFGEN_RSTAT) & - I40E_VFGEN_RSTAT_VFR_STATE_MASK; - if (!(adapter->flags & I40EVF_FLAG_RESET_PENDING) && - (rstat_val != I40E_VFR_VFACTIVE) && - (rstat_val != I40E_VFR_COMPLETED)) { + reg_val = rd32(hw, I40E_VF_ARQLEN1) & I40E_VF_ARQLEN1_ARQENABLE_MASK; + if (!(adapter->flags & I40EVF_FLAG_RESET_PENDING) && !reg_val) { adapter->state = __I40EVF_RESETTING; adapter->flags |= I40EVF_FLAG_RESET_PENDING; dev_err(&adapter->pdev->dev, "Hardware reset detected\n"); @@ -1572,7 +1569,7 @@ static void i40evf_reset_task(struct work_struct *work) struct net_device *netdev = adapter->netdev; struct i40e_hw *hw = &adapter->hw; struct i40evf_mac_filter *f; - uint32_t rstat_val; + u32 reg_val; int i = 0, err; while (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK, @@ -1593,12 +1590,11 @@ static void i40evf_reset_task(struct work_struct *work) /* poll until we see the reset actually happen */ for (i = 0; i < I40EVF_RESET_WAIT_COUNT; i++) { - rstat_val = rd32(hw, I40E_VFGEN_RSTAT) & - I40E_VFGEN_RSTAT_VFR_STATE_MASK; - if ((rstat_val != I40E_VFR_VFACTIVE) && - (rstat_val != I40E_VFR_COMPLETED)) + reg_val = rd32(hw, I40E_VF_ARQLEN1) & + I40E_VF_ARQLEN1_ARQENABLE_MASK; + if (!reg_val) break; - usleep_range(500, 1000); + usleep_range(5000, 10000); } if (i == I40EVF_RESET_WAIT_COUNT) { dev_info(&adapter->pdev->dev, "Never saw reset\n"); @@ -1607,9 +1603,9 @@ 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++) { - rstat_val = rd32(hw, I40E_VFGEN_RSTAT) & - I40E_VFGEN_RSTAT_VFR_STATE_MASK; - if (rstat_val == I40E_VFR_VFACTIVE) + 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); } @@ -1621,7 +1617,7 @@ static void i40evf_reset_task(struct work_struct *work) /* reset never finished */ dev_err(&adapter->pdev->dev, "Reset never finished (%x)\n", - rstat_val); + reg_val); adapter->flags |= I40EVF_FLAG_PF_COMMS_FAILED; if (netif_running(adapter->netdev)) { -- cgit v0.10.2 From 6995b36c0fc3dd97c1d641f9630d19db2cadf44f Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Fri, 28 Aug 2015 17:55:54 -0400 Subject: i40e/i40evf: clean up some code Add missings spaces after declarations, remove another __func__ use, remove uncessary braces, remove unneeded breaks, and useless returns, and generally fix up some code. Change-ID: Ie715d6b64976c50e1c21531685fe0a2bd38c4244 Signed-off-by: Jesse Brandeburg Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.c b/drivers/net/ethernet/intel/i40e/i40e_adminq.c index 287cb8d..fa2e916 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq.c +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.c @@ -683,8 +683,7 @@ static u16 i40e_clean_asq(struct i40e_hw *hw) details = I40E_ADMINQ_DETAILS(*asq, ntc); while (rd32(hw, hw->aq.asq.head) != ntc) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, - "%s: ntc %d head %d.\n", __func__, ntc, - rd32(hw, hw->aq.asq.head)); + "ntc %d head %d.\n", ntc, rd32(hw, hw->aq.asq.head)); if (details->callback) { I40E_ADMINQ_CALLBACK cb_func = diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 2839ea5..2d012d9 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -1035,7 +1035,7 @@ i40e_status i40e_get_mac_addr(struct i40e_hw *hw, u8 *mac_addr) status = i40e_aq_mac_address_read(hw, &flags, &addrs, NULL); if (flags & I40E_AQC_LAN_ADDR_VALID) - memcpy(mac_addr, &addrs.pf_lan_mac, sizeof(addrs.pf_lan_mac)); + ether_addr_copy(mac_addr, addrs.pf_lan_mac); return status; } @@ -1058,7 +1058,7 @@ i40e_status i40e_get_port_mac_addr(struct i40e_hw *hw, u8 *mac_addr) return status; if (flags & I40E_AQC_PORT_ADDR_VALID) - memcpy(mac_addr, &addrs.port_mac, sizeof(addrs.port_mac)); + ether_addr_copy(mac_addr, addrs.port_mac); else status = I40E_ERR_INVALID_MAC_ADDR; @@ -1116,7 +1116,7 @@ i40e_status i40e_get_san_mac_addr(struct i40e_hw *hw, u8 *mac_addr) return status; if (flags & I40E_AQC_SAN_ADDR_VALID) - memcpy(mac_addr, &addrs.pf_san_mac, sizeof(addrs.pf_san_mac)); + ether_addr_copy(mac_addr, addrs.pf_san_mac); else status = I40E_ERR_INVALID_MAC_ADDR; @@ -2363,6 +2363,7 @@ i40e_status i40e_aq_get_veb_parameters(struct i40e_hw *hw, *vebs_free = le16_to_cpu(cmd_resp->vebs_free); if (floating) { u16 flags = le16_to_cpu(cmd_resp->veb_flags); + if (flags & I40E_AQC_ADD_VEB_FLOATING) *floating = true; else @@ -3777,7 +3778,7 @@ i40e_status i40e_aq_add_rem_control_packet_filter(struct i40e_hw *hw, } if (mac_addr) - memcpy(cmd->mac, mac_addr, ETH_ALEN); + ether_addr_copy(cmd->mac, mac_addr); cmd->etype = cpu_to_le16(ethtype); cmd->flags = cpu_to_le16(flags); diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c b/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c index dbadad7..7c42d13 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c @@ -236,6 +236,7 @@ static void i40e_dcbnl_del_app(struct i40e_pf *pf, struct i40e_dcb_app_priority_table *app) { int v, err; + for (v = 0; v < pf->num_alloc_vsi; v++) { if (pf->vsi[v] && pf->vsi[v]->netdev) { err = i40e_dcbnl_vsi_del_app(pf->vsi[v], app); diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c index 9f9d842..c1dd248 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c +++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c @@ -404,82 +404,82 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) nstat = i40e_get_vsi_stats_struct(vsi); dev_info(&pf->pdev->dev, " net_stats: rx_packets = %lu, rx_bytes = %lu, rx_errors = %lu, rx_dropped = %lu\n", - (long unsigned int)nstat->rx_packets, - (long unsigned int)nstat->rx_bytes, - (long unsigned int)nstat->rx_errors, - (long unsigned int)nstat->rx_dropped); + (unsigned long int)nstat->rx_packets, + (unsigned long int)nstat->rx_bytes, + (unsigned long int)nstat->rx_errors, + (unsigned long int)nstat->rx_dropped); dev_info(&pf->pdev->dev, " net_stats: tx_packets = %lu, tx_bytes = %lu, tx_errors = %lu, tx_dropped = %lu\n", - (long unsigned int)nstat->tx_packets, - (long unsigned int)nstat->tx_bytes, - (long unsigned int)nstat->tx_errors, - (long unsigned int)nstat->tx_dropped); + (unsigned long int)nstat->tx_packets, + (unsigned long int)nstat->tx_bytes, + (unsigned long int)nstat->tx_errors, + (unsigned long int)nstat->tx_dropped); dev_info(&pf->pdev->dev, " net_stats: multicast = %lu, collisions = %lu\n", - (long unsigned int)nstat->multicast, - (long unsigned int)nstat->collisions); + (unsigned long int)nstat->multicast, + (unsigned long int)nstat->collisions); dev_info(&pf->pdev->dev, " net_stats: rx_length_errors = %lu, rx_over_errors = %lu, rx_crc_errors = %lu\n", - (long unsigned int)nstat->rx_length_errors, - (long unsigned int)nstat->rx_over_errors, - (long unsigned int)nstat->rx_crc_errors); + (unsigned long int)nstat->rx_length_errors, + (unsigned long int)nstat->rx_over_errors, + (unsigned long int)nstat->rx_crc_errors); dev_info(&pf->pdev->dev, " net_stats: rx_frame_errors = %lu, rx_fifo_errors = %lu, rx_missed_errors = %lu\n", - (long unsigned int)nstat->rx_frame_errors, - (long unsigned int)nstat->rx_fifo_errors, - (long unsigned int)nstat->rx_missed_errors); + (unsigned long int)nstat->rx_frame_errors, + (unsigned long int)nstat->rx_fifo_errors, + (unsigned long int)nstat->rx_missed_errors); dev_info(&pf->pdev->dev, " net_stats: tx_aborted_errors = %lu, tx_carrier_errors = %lu, tx_fifo_errors = %lu\n", - (long unsigned int)nstat->tx_aborted_errors, - (long unsigned int)nstat->tx_carrier_errors, - (long unsigned int)nstat->tx_fifo_errors); + (unsigned long int)nstat->tx_aborted_errors, + (unsigned long int)nstat->tx_carrier_errors, + (unsigned long int)nstat->tx_fifo_errors); dev_info(&pf->pdev->dev, " net_stats: tx_heartbeat_errors = %lu, tx_window_errors = %lu\n", - (long unsigned int)nstat->tx_heartbeat_errors, - (long unsigned int)nstat->tx_window_errors); + (unsigned long int)nstat->tx_heartbeat_errors, + (unsigned long int)nstat->tx_window_errors); dev_info(&pf->pdev->dev, " net_stats: rx_compressed = %lu, tx_compressed = %lu\n", - (long unsigned int)nstat->rx_compressed, - (long unsigned int)nstat->tx_compressed); + (unsigned long int)nstat->rx_compressed, + (unsigned long int)nstat->tx_compressed); dev_info(&pf->pdev->dev, " net_stats_offsets: rx_packets = %lu, rx_bytes = %lu, rx_errors = %lu, rx_dropped = %lu\n", - (long unsigned int)vsi->net_stats_offsets.rx_packets, - (long unsigned int)vsi->net_stats_offsets.rx_bytes, - (long unsigned int)vsi->net_stats_offsets.rx_errors, - (long unsigned int)vsi->net_stats_offsets.rx_dropped); + (unsigned long int)vsi->net_stats_offsets.rx_packets, + (unsigned long int)vsi->net_stats_offsets.rx_bytes, + (unsigned long int)vsi->net_stats_offsets.rx_errors, + (unsigned long int)vsi->net_stats_offsets.rx_dropped); dev_info(&pf->pdev->dev, " net_stats_offsets: tx_packets = %lu, tx_bytes = %lu, tx_errors = %lu, tx_dropped = %lu\n", - (long unsigned int)vsi->net_stats_offsets.tx_packets, - (long unsigned int)vsi->net_stats_offsets.tx_bytes, - (long unsigned int)vsi->net_stats_offsets.tx_errors, - (long unsigned int)vsi->net_stats_offsets.tx_dropped); + (unsigned long int)vsi->net_stats_offsets.tx_packets, + (unsigned long int)vsi->net_stats_offsets.tx_bytes, + (unsigned long int)vsi->net_stats_offsets.tx_errors, + (unsigned long int)vsi->net_stats_offsets.tx_dropped); dev_info(&pf->pdev->dev, " net_stats_offsets: multicast = %lu, collisions = %lu\n", - (long unsigned int)vsi->net_stats_offsets.multicast, - (long unsigned int)vsi->net_stats_offsets.collisions); + (unsigned long int)vsi->net_stats_offsets.multicast, + (unsigned long int)vsi->net_stats_offsets.collisions); dev_info(&pf->pdev->dev, " net_stats_offsets: rx_length_errors = %lu, rx_over_errors = %lu, rx_crc_errors = %lu\n", - (long unsigned int)vsi->net_stats_offsets.rx_length_errors, - (long unsigned int)vsi->net_stats_offsets.rx_over_errors, - (long unsigned int)vsi->net_stats_offsets.rx_crc_errors); + (unsigned long int)vsi->net_stats_offsets.rx_length_errors, + (unsigned long int)vsi->net_stats_offsets.rx_over_errors, + (unsigned long int)vsi->net_stats_offsets.rx_crc_errors); dev_info(&pf->pdev->dev, " net_stats_offsets: rx_frame_errors = %lu, rx_fifo_errors = %lu, rx_missed_errors = %lu\n", - (long unsigned int)vsi->net_stats_offsets.rx_frame_errors, - (long unsigned int)vsi->net_stats_offsets.rx_fifo_errors, - (long unsigned int)vsi->net_stats_offsets.rx_missed_errors); + (unsigned long int)vsi->net_stats_offsets.rx_frame_errors, + (unsigned long int)vsi->net_stats_offsets.rx_fifo_errors, + (unsigned long int)vsi->net_stats_offsets.rx_missed_errors); dev_info(&pf->pdev->dev, " net_stats_offsets: tx_aborted_errors = %lu, tx_carrier_errors = %lu, tx_fifo_errors = %lu\n", - (long unsigned int)vsi->net_stats_offsets.tx_aborted_errors, - (long unsigned int)vsi->net_stats_offsets.tx_carrier_errors, - (long unsigned int)vsi->net_stats_offsets.tx_fifo_errors); + (unsigned long int)vsi->net_stats_offsets.tx_aborted_errors, + (unsigned long int)vsi->net_stats_offsets.tx_carrier_errors, + (unsigned long int)vsi->net_stats_offsets.tx_fifo_errors); dev_info(&pf->pdev->dev, " net_stats_offsets: tx_heartbeat_errors = %lu, tx_window_errors = %lu\n", - (long unsigned int)vsi->net_stats_offsets.tx_heartbeat_errors, - (long unsigned int)vsi->net_stats_offsets.tx_window_errors); + (unsigned long int)vsi->net_stats_offsets.tx_heartbeat_errors, + (unsigned long int)vsi->net_stats_offsets.tx_window_errors); dev_info(&pf->pdev->dev, " net_stats_offsets: rx_compressed = %lu, tx_compressed = %lu\n", - (long unsigned int)vsi->net_stats_offsets.rx_compressed, - (long unsigned int)vsi->net_stats_offsets.tx_compressed); + (unsigned long int)vsi->net_stats_offsets.rx_compressed, + (unsigned long int)vsi->net_stats_offsets.tx_compressed); dev_info(&pf->pdev->dev, " tx_restart = %d, tx_busy = %d, rx_buf_failed = %d, rx_page_failed = %d\n", vsi->tx_restart, vsi->tx_busy, @@ -487,6 +487,7 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) rcu_read_lock(); for (i = 0; i < vsi->num_queue_pairs; i++) { struct i40e_ring *rx_ring = ACCESS_ONCE(vsi->rx_rings[i]); + if (!rx_ring) continue; @@ -527,7 +528,7 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) dev_info(&pf->pdev->dev, " rx_rings[%i]: size = %i, dma = 0x%08lx\n", i, rx_ring->size, - (long unsigned int)rx_ring->dma); + (unsigned long int)rx_ring->dma); dev_info(&pf->pdev->dev, " rx_rings[%i]: vsi = %p, q_vector = %p\n", i, rx_ring->vsi, @@ -535,6 +536,7 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) } for (i = 0; i < vsi->num_queue_pairs; i++) { struct i40e_ring *tx_ring = ACCESS_ONCE(vsi->tx_rings[i]); + if (!tx_ring) continue; @@ -573,7 +575,7 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) dev_info(&pf->pdev->dev, " tx_rings[%i]: size = %i, dma = 0x%08lx\n", i, tx_ring->size, - (long unsigned int)tx_ring->dma); + (unsigned long int)tx_ring->dma); dev_info(&pf->pdev->dev, " tx_rings[%i]: vsi = %p, q_vector = %p\n", i, tx_ring->vsi, @@ -743,6 +745,7 @@ static void i40e_dbg_dump_aq_desc(struct i40e_pf *pf) ring = &(hw->aq.asq); for (i = 0; i < ring->count; i++) { struct i40e_aq_desc *d = I40E_ADMINQ_DESC(*ring, i); + dev_info(&pf->pdev->dev, " at[%02d] flags=0x%04x op=0x%04x dlen=0x%04x ret=0x%04x cookie_h=0x%08x cookie_l=0x%08x\n", i, d->flags, d->opcode, d->datalen, d->retval, @@ -755,6 +758,7 @@ static void i40e_dbg_dump_aq_desc(struct i40e_pf *pf) ring = &(hw->aq.arq); for (i = 0; i < ring->count; i++) { struct i40e_aq_desc *d = I40E_ADMINQ_DESC(*ring, i); + dev_info(&pf->pdev->dev, " ar[%02d] flags=0x%04x op=0x%04x dlen=0x%04x ret=0x%04x cookie_h=0x%08x cookie_l=0x%08x\n", i, d->flags, d->opcode, d->datalen, d->retval, @@ -1038,7 +1042,13 @@ static ssize_t i40e_dbg_command_write(struct file *filp, dev_info(&pf->pdev->dev, "'%s' failed\n", cmd_buf); } else if (strncmp(cmd_buf, "del vsi", 7) == 0) { - sscanf(&cmd_buf[7], "%i", &vsi_seid); + cnt = sscanf(&cmd_buf[7], "%i", &vsi_seid); + if (cnt != 1) { + dev_info(&pf->pdev->dev, + "del vsi: 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 VSI %d: seid not found\n", @@ -1488,6 +1498,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } else if (strncmp(cmd_buf, "read", 4) == 0) { u32 address; u32 value; + cnt = sscanf(&cmd_buf[4], "%i", &address); if (cnt != 1) { dev_info(&pf->pdev->dev, "read \n"); @@ -1507,6 +1518,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } else if (strncmp(cmd_buf, "write", 5) == 0) { u32 address, value; + cnt = sscanf(&cmd_buf[5], "%i %i", &address, &value); if (cnt != 2) { dev_info(&pf->pdev->dev, "write \n"); @@ -1528,6 +1540,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, cnt = sscanf(&cmd_buf[15], "%i", &vsi_seid); if (cnt == 0) { int i; + for (i = 0; i < pf->num_alloc_vsi; i++) i40e_vsi_reset_stats(pf->vsi[i]); dev_info(&pf->pdev->dev, "vsi clear stats called for all vsi's\n"); @@ -1726,8 +1739,9 @@ static ssize_t i40e_dbg_command_write(struct file *filp, packet_len, I40E_FDIR_MAX_RAW_PACKET_SIZE); for (i = 0; i < packet_len; i++) { - sscanf(&asc_packet[j], "%2hhx ", - &raw_packet[i]); + cnt = sscanf(&asc_packet[j], "%2hhx ", &raw_packet[i]); + if (!cnt) + break; j += 3; } dev_info(&pf->pdev->dev, "FD raw packet dump\n"); @@ -1755,6 +1769,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } else if (strncmp(cmd_buf, "lldp", 4) == 0) { if (strncmp(&cmd_buf[5], "stop", 4) == 0) { int ret; + ret = i40e_aq_stop_lldp(&pf->hw, false, NULL); if (ret) { dev_info(&pf->pdev->dev, @@ -1779,6 +1794,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, #endif /* CONFIG_I40E_DCB */ } else if (strncmp(&cmd_buf[5], "start", 5) == 0) { int ret; + ret = i40e_aq_add_rem_control_packet_filter(&pf->hw, pf->hw.mac.addr, I40E_ETH_P_LLDP, 0, @@ -1807,6 +1823,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, u16 llen, rlen; int ret; u8 *buff; + buff = kzalloc(I40E_LLDPDU_SIZE, GFP_KERNEL); if (!buff) goto command_write_done; @@ -1833,6 +1850,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, u16 llen, rlen; int ret; u8 *buff; + buff = kzalloc(I40E_LLDPDU_SIZE, GFP_KERNEL); if (!buff) goto command_write_done; @@ -1858,6 +1876,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, buff = NULL; } else if (strncmp(&cmd_buf[5], "event on", 8) == 0) { int ret; + ret = i40e_aq_cfg_lldp_mib_change_event(&pf->hw, true, NULL); if (ret) { @@ -1868,6 +1887,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } } else if (strncmp(&cmd_buf[5], "event off", 9) == 0) { int ret; + ret = i40e_aq_cfg_lldp_mib_change_event(&pf->hw, false, NULL); if (ret) { @@ -2105,6 +2125,7 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp, } } else if (strncmp(i40e_dbg_netdev_ops_buf, "change_mtu", 10) == 0) { int mtu; + cnt = sscanf(&i40e_dbg_netdev_ops_buf[11], "%i %i", &vsi_seid, &mtu); if (cnt != 2) { @@ -2220,7 +2241,6 @@ void i40e_dbg_pf_init(struct i40e_pf *pf) create_failed: dev_info(dev, "debugfs dir/file for %s failed\n", name); debugfs_remove_recursive(pf->i40e_dbg_pf); - return; } /** diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 5a726f2..148f614 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -1360,6 +1360,7 @@ static void i40e_get_ethtool_stats(struct net_device *netdev, if ((pf->lan_veb != I40E_NO_VEB) && (pf->flags & I40E_FLAG_VEB_STATS_ENABLED)) { struct i40e_veb *veb = pf->veb[pf->lan_veb]; + for (j = 0; j < I40E_VEB_STATS_LEN; j++) { p = (char *)veb; p += i40e_gstrings_veb_stats[j].stat_offset; @@ -1607,7 +1608,7 @@ static inline bool i40e_active_vfs(struct i40e_pf *pf) int i; for (i = 0; i < pf->num_alloc_vfs; i++) - if (vfs[i].vf_states & I40E_VF_STAT_ACTIVE) + if (test_bit(I40E_VF_STAT_ACTIVE, &vfs[i].vf_states)) return true; return false; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c b/drivers/net/ethernet/intel/i40e/i40e_fcoe.c index 2398d9b..eaedc1f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c +++ b/drivers/net/ethernet/intel/i40e/i40e_fcoe.c @@ -324,7 +324,6 @@ void i40e_init_pf_fcoe(struct i40e_pf *pf) wr32(hw, I40E_GLFCOE_RCTL, val); dev_info(&pf->pdev->dev, "FCoE is supported.\n"); - return; } /** diff --git a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c index fa371a2..79ae7be 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c +++ b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c @@ -431,9 +431,8 @@ exit_sd_error: pd_idx1 = max(pd_idx, ((j - 1) * I40E_HMC_MAX_BP_COUNT)); pd_lmt1 = min(pd_lmt, (j * I40E_HMC_MAX_BP_COUNT)); - for (i = pd_idx1; i < pd_lmt1; i++) { + for (i = pd_idx1; i < pd_lmt1; i++) i40e_remove_pd_bp(hw, info->hmc_info, i); - } i40e_remove_pd_page(hw, info->hmc_info, (j - 1)); break; case I40E_SD_TYPE_DIRECT: diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 23a7b40..12b90fa 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -216,10 +216,10 @@ static int i40e_get_lump(struct i40e_pf *pf, struct i40e_lump_tracking *pile, ret = i; pile->search_hint = i + j; break; - } else { - /* not enough, so skip over it and continue looking */ - i += j; } + + /* not enough, so skip over it and continue looking */ + i += j; } return ret; @@ -503,11 +503,11 @@ void i40e_vsi_reset_stats(struct i40e_vsi *vsi) memset(&vsi->eth_stats_offsets, 0, sizeof(vsi->eth_stats_offsets)); if (vsi->rx_rings && vsi->rx_rings[0]) { for (i = 0; i < vsi->num_queue_pairs; i++) { - memset(&vsi->rx_rings[i]->stats, 0 , + memset(&vsi->rx_rings[i]->stats, 0, sizeof(vsi->rx_rings[i]->stats)); - memset(&vsi->rx_rings[i]->rx_stats, 0 , + memset(&vsi->rx_rings[i]->rx_stats, 0, sizeof(vsi->rx_rings[i]->rx_stats)); - memset(&vsi->tx_rings[i]->stats, 0 , + memset(&vsi->tx_rings[i]->stats, 0, sizeof(vsi->tx_rings[i]->stats)); memset(&vsi->tx_rings[i]->tx_stats, 0, sizeof(vsi->tx_rings[i]->tx_stats)); @@ -843,6 +843,7 @@ static void i40e_update_prio_xoff_rx(struct i40e_pf *pf) for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) { u64 prio_xoff = nsd->priority_xoff_rx[i]; + i40e_stat_update32(hw, I40E_GLPRT_PXOFFRXC(hw->port, i), pf->stat_offsets_loaded, &osd->priority_xoff_rx[i], @@ -1439,6 +1440,7 @@ void i40e_del_filter(struct i40e_vsi *vsi, } 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); @@ -1497,6 +1499,7 @@ static int i40e_set_mac(struct net_device *netdev, void *p) if (vsi->type == I40E_VSI_MAIN) { i40e_status ret; + ret = i40e_aq_mac_address_write(&vsi->back->hw, I40E_AQC_WRITE_TYPE_LAA_WOL, addr->sa_data, NULL); @@ -1932,6 +1935,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl) /* check for changes in promiscuous modes */ if (changed_flags & IFF_ALLMULTI) { bool cur_multipromisc; + cur_multipromisc = !!(vsi->current_netdev_flags & IFF_ALLMULTI); ret = i40e_aq_set_vsi_multicast_promiscuous(&vsi->back->hw, vsi->seid, @@ -1946,6 +1950,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl) } if ((changed_flags & IFF_PROMISC) || promisc_forced_on) { bool cur_promisc; + cur_promisc = (!!(vsi->current_netdev_flags & IFF_PROMISC) || test_bit(__I40E_FILTER_OVERFLOW_PROMISC, &vsi->state)); @@ -3259,6 +3264,7 @@ static irqreturn_t i40e_intr(int irq, void *data) /* temporarily disable queue cause for NAPI processing */ u32 qval = rd32(hw, I40E_QINT_RQCTL(0)); + qval &= ~I40E_QINT_RQCTL_CAUSE_ENA_MASK; wr32(hw, I40E_QINT_RQCTL(0), qval); @@ -3428,9 +3434,9 @@ static bool i40e_clean_fdir_tx_irq(struct i40e_ring *tx_ring, int budget) i += tx_ring->count; tx_ring->next_to_clean = i; - if (vsi->back->flags & I40E_FLAG_MSIX_ENABLED) { + if (vsi->back->flags & I40E_FLAG_MSIX_ENABLED) i40e_irq_dynamic_enable(vsi, tx_ring->q_vector->v_idx); - } + return budget > 0; } @@ -4034,11 +4040,10 @@ static void i40e_quiesce_vsi(struct i40e_vsi *vsi) } set_bit(__I40E_NEEDS_RESTART, &vsi->state); - if (vsi->netdev && netif_running(vsi->netdev)) { + if (vsi->netdev && netif_running(vsi->netdev)) vsi->netdev->netdev_ops->ndo_stop(vsi->netdev); - } else { + else i40e_vsi_close(vsi); - } } /** @@ -5313,15 +5318,13 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags) "VSI reinit requested\n"); for (v = 0; v < pf->num_alloc_vsi; v++) { struct i40e_vsi *vsi = pf->vsi[v]; + if (vsi != NULL && test_bit(__I40E_REINIT_REQUESTED, &vsi->state)) { i40e_vsi_reinit_locked(pf->vsi[v]); clear_bit(__I40E_REINIT_REQUESTED, &vsi->state); } } - - /* no further action needed, so return now */ - return; } else if (reset_flags & BIT_ULL(__I40E_DOWN_REQUESTED)) { int v; @@ -5329,6 +5332,7 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags) dev_info(&pf->pdev->dev, "VSI down requested\n"); for (v = 0; v < pf->num_alloc_vsi; v++) { struct i40e_vsi *vsi = pf->vsi[v]; + if (vsi != NULL && test_bit(__I40E_DOWN_REQUESTED, &vsi->state)) { set_bit(__I40E_DOWN, &vsi->state); @@ -5336,13 +5340,9 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags) clear_bit(__I40E_DOWN_REQUESTED, &vsi->state); } } - - /* no further action needed, so return now */ - return; } else { dev_info(&pf->pdev->dev, "bad reset request 0x%08x\n", reset_flags); - return; } } @@ -6286,6 +6286,7 @@ static int i40e_reconstitute_veb(struct i40e_veb *veb) if (pf->vsi[v]->veb_idx == veb->idx) { struct i40e_vsi *vsi = pf->vsi[v]; + vsi->uplink_seid = veb->seid; ret = i40e_add_vsi(vsi); if (ret) { @@ -9700,6 +9701,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit) } else { /* force a reset of TC and queue layout configurations */ u8 enabled_tc = pf->vsi[pf->lan_vsi]->tc_config.enabled_tc; + 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); diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index 8c40d6e..552c84e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -618,9 +618,8 @@ static long i40e_ptp_create_clock(struct i40e_pf *pf) /* Attempt to register the clock before enabling the hardware. */ pf->ptp_clock = ptp_clock_register(&pf->ptp_caps, &pf->pdev->dev); - if (IS_ERR(pf->ptp_clock)) { + if (IS_ERR(pf->ptp_clock)) return PTR_ERR(pf->ptp_clock); - } /* clear the hwtstamp settings here during clock create, instead of * during regular init, so that we can maintain settings across a diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 8ab7ab1..01e5ece 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1522,6 +1522,7 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget) cleaned_count++; if (rx_hbo || rx_sph) { int len; + if (rx_hbo) len = I40E_RX_HDR_SIZE; else @@ -1707,9 +1708,6 @@ static int i40e_clean_rx_irq_1buf(struct i40e_ring *rx_ring, int budget) /* ERR_MASK will only have valid bits if EOP set */ if (unlikely(rx_error & BIT(I40E_RX_DESC_ERROR_RXE_SHIFT))) { dev_kfree_skb_any(skb); - /* TODO: shouldn't we increment a counter indicating the - * drop? - */ continue; } @@ -2081,6 +2079,7 @@ static inline int i40e_tx_prepare_vlan_flags(struct sk_buff *skb, /* else if it is a SW VLAN, check the next protocol and store the tag */ } else if (protocol == htons(ETH_P_8021Q)) { struct vlan_hdr *vhdr, _vhdr; + vhdr = skb_header_pointer(skb, ETH_HLEN, sizeof(_vhdr), &_vhdr); if (!vhdr) return -EINVAL; @@ -2740,6 +2739,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, u8 hdr_len = 0; int tsyn; int tso; + if (0 == i40e_xmit_descriptor_count(skb, tx_ring)) return NETDEV_TX_BUSY; diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index ac3fb3a..75cecfa 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -165,6 +165,7 @@ struct i40e_tx_buffer { }; unsigned int bytecount; unsigned short gso_segs; + DEFINE_DMA_UNMAP_ADDR(dma); DEFINE_DMA_UNMAP_LEN(len); u32 tx_flags; diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index fac8a02..678623f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -536,6 +536,7 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type) } if (type == I40E_VSI_SRIOV) { u8 brdcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + vf->lan_vsi_idx = vsi->idx; vf->lan_vsi_id = vsi->id; /* If the port VLAN has been configured and then the @@ -605,6 +606,7 @@ static void i40e_enable_vf_mappings(struct i40e_vf *vf) /* map PF queues to VF queues */ for (j = 0; j < pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs; j++) { u16 qid = i40e_vc_get_pf_queue_id(vf, vf->lan_vsi_id, j); + reg = (qid & I40E_VPLAN_QTABLE_QINDEX_MASK); wr32(hw, I40E_VPLAN_QTABLE(total_queue_pairs, vf->vf_id), reg); total_queue_pairs++; @@ -991,7 +993,7 @@ static int i40e_pci_sriov_enable(struct pci_dev *pdev, int num_vfs) int pre_existing_vfs = pci_num_vf(pdev); int err = 0; - if (pf->state & __I40E_TESTING) { + if (test_bit(__I40E_TESTING, &pf->state)) { dev_warn(&pdev->dev, "Cannot enable SR-IOV virtual functions while the device is undergoing diagnostic testing\n"); err = -EPERM; @@ -1210,8 +1212,8 @@ static int i40e_vc_get_vf_resources_msg(struct i40e_vf *vf, u8 *msg) vfres->vsi_res[i].vsi_type = I40E_VSI_SRIOV; vfres->vsi_res[i].num_queue_pairs = pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs; - memcpy(vfres->vsi_res[i].default_mac_addr, - vf->default_lan_addr.addr, ETH_ALEN); + ether_addr_copy(vfres->vsi_res[i].default_mac_addr, + vf->default_lan_addr.addr); i++; } set_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states); @@ -1715,6 +1717,7 @@ static int i40e_vc_add_vlan_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) for (i = 0; i < vfl->num_elements; i++) { /* add new VLAN filter */ int ret = i40e_vsi_add_vlan(vsi, vfl->vlan_id[i]); + if (ret) dev_err(&pf->pdev->dev, "Unable to add VF vlan filter %d, error %d\n", @@ -1766,6 +1769,7 @@ 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) dev_err(&pf->pdev->dev, "Unable to delete VF vlan filter %d, error %d\n", @@ -1877,7 +1881,6 @@ static int i40e_vc_validate_vf_msg(struct i40e_vf *vf, u32 v_opcode, case I40E_VIRTCHNL_OP_UNKNOWN: default: return -EPERM; - break; } /* few more checks */ if ((valid_len != msglen) || (err_msg_format)) { @@ -2316,7 +2319,7 @@ int i40e_ndo_get_vf_config(struct net_device *netdev, ivi->vf = vf_id; - memcpy(&ivi->mac, vf->default_lan_addr.addr, ETH_ALEN); + ether_addr_copy(ivi->mac, vf->default_lan_addr.addr); ivi->max_tx_rate = vf->tx_rate; ivi->min_tx_rate = 0; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq.c b/drivers/net/ethernet/intel/i40evf/i40e_adminq.c index c5cf886..3eba369 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq.c @@ -622,8 +622,7 @@ static u16 i40e_clean_asq(struct i40e_hw *hw) details = I40E_ADMINQ_DETAILS(*asq, ntc); while (rd32(hw, hw->aq.asq.head) != ntc) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, - "%s: ntc %d head %d.\n", __func__, ntc, - rd32(hw, hw->aq.asq.head)); + "ntc %d head %d.\n", ntc, rd32(hw, hw->aq.asq.head)); if (details->callback) { I40E_ADMINQ_CALLBACK cb_func = diff --git a/drivers/net/ethernet/intel/i40evf/i40e_common.c b/drivers/net/ethernet/intel/i40evf/i40e_common.c index 96e48ee..b98b642 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_common.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_common.c @@ -992,10 +992,10 @@ void i40e_vf_parse_hw_config(struct i40e_hw *hw, I40E_VIRTCHNL_VF_OFFLOAD_FCOE) ? 1 : 0; for (i = 0; i < msg->num_vsis; i++) { if (vsi_res->vsi_type == I40E_VSI_SRIOV) { - memcpy(hw->mac.perm_addr, vsi_res->default_mac_addr, - ETH_ALEN); - memcpy(hw->mac.addr, vsi_res->default_mac_addr, - ETH_ALEN); + ether_addr_copy(hw->mac.perm_addr, + vsi_res->default_mac_addr); + ether_addr_copy(hw->mac.addr, + vsi_res->default_mac_addr); } vsi_res++; } diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 3b102f2..0e71eb4 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -987,6 +987,7 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget) cleaned_count++; if (rx_hbo || rx_sph) { int len; + if (rx_hbo) len = I40E_RX_HDR_SIZE; else @@ -1160,9 +1161,6 @@ static int i40e_clean_rx_irq_1buf(struct i40e_ring *rx_ring, int budget) /* ERR_MASK will only have valid bits if EOP set */ if (unlikely(rx_error & BIT(I40E_RX_DESC_ERROR_RXE_SHIFT))) { dev_kfree_skb_any(skb); - /* TODO: shouldn't we increment a counter indicating the - * drop? - */ continue; } @@ -1358,6 +1356,7 @@ static inline int i40evf_tx_prepare_vlan_flags(struct sk_buff *skb, /* else if it is a SW VLAN, check the next protocol and store the tag */ } else if (protocol == htons(ETH_P_8021Q)) { struct vlan_hdr *vhdr, _vhdr; + vhdr = skb_header_pointer(skb, ETH_HLEN, sizeof(_vhdr), &_vhdr); if (!vhdr) return -EINVAL; @@ -1900,6 +1899,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, u32 td_cmd = 0; u8 hdr_len = 0; int tso; + if (0 == i40evf_xmit_descriptor_count(skb, tx_ring)) return NETDEV_TX_BUSY; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index 93b90f7..0c13ece 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -164,6 +164,7 @@ struct i40e_tx_buffer { }; unsigned int bytecount; unsigned short gso_segs; + DEFINE_DMA_UNMAP_ADDR(dma); DEFINE_DMA_UNMAP_LEN(len); u32 tx_flags; diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index 3817cbb..27dc3fe 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -48,10 +48,6 @@ #define DEFAULT_DEBUG_LEVEL_SHIFT 3 #define PFX "i40evf: " -#define DPRINTK(nlevel, klevel, fmt, args...) \ - ((void)((NETIF_MSG_##nlevel & adapter->msg_enable) && \ - printk(KERN_##klevel PFX "%s: %s: " fmt, adapter->netdev->name, \ - __func__ , ## args))) /* dummy struct to make common code less painful */ struct i40e_vsi { -- cgit v0.10.2 From a5fdaf342aa2fa6679dcb87dad2f78f1309de29e Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Fri, 28 Aug 2015 17:55:56 -0400 Subject: i40e: refactor code to remove indent I found a code indent that was avoidable because a whole function is inside an if block, reverse the if and move the code back a tab. Change-ID: I9989c8750ee61678fbf96a3b0fd7bf7cc7ef300a Signed-off-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 12b90fa..c46d814 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -5679,49 +5679,51 @@ static void i40e_fdir_flush_and_replay(struct i40e_pf *pf) if (!(pf->flags & (I40E_FLAG_FD_SB_ENABLED | I40E_FLAG_FD_ATR_ENABLED))) return; - if (time_after(jiffies, pf->fd_flush_timestamp + - (I40E_MIN_FD_FLUSH_INTERVAL * HZ))) { - /* If the flush is happening too quick and we have mostly - * SB rules we should not re-enable ATR for some time. - */ - min_flush_time = pf->fd_flush_timestamp - + (I40E_MIN_FD_FLUSH_SB_ATR_UNSTABLE * HZ); - fd_room = pf->fdir_pf_filter_count - pf->fdir_pf_active_filters; + if (!time_after(jiffies, pf->fd_flush_timestamp + + (I40E_MIN_FD_FLUSH_INTERVAL * HZ))) + return; - if (!(time_after(jiffies, min_flush_time)) && - (fd_room < I40E_FDIR_BUFFER_HEAD_ROOM_FOR_ATR)) { - if (I40E_DEBUG_FD & pf->hw.debug_mask) - dev_info(&pf->pdev->dev, "ATR disabled, not enough FD filter space.\n"); - disable_atr = true; - } + /* If the flush is happening too quick and we have mostly SB rules we + * should not re-enable ATR for some time. + */ + min_flush_time = pf->fd_flush_timestamp + + (I40E_MIN_FD_FLUSH_SB_ATR_UNSTABLE * HZ); + fd_room = pf->fdir_pf_filter_count - pf->fdir_pf_active_filters; - pf->fd_flush_timestamp = jiffies; - pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED; - /* flush all filters */ - wr32(&pf->hw, I40E_PFQF_CTL_1, - I40E_PFQF_CTL_1_CLEARFDTABLE_MASK); - i40e_flush(&pf->hw); - pf->fd_flush_cnt++; - pf->fd_add_err = 0; - do { - /* Check FD flush status every 5-6msec */ - usleep_range(5000, 6000); - reg = rd32(&pf->hw, I40E_PFQF_CTL_1); - if (!(reg & I40E_PFQF_CTL_1_CLEARFDTABLE_MASK)) - break; - } while (flush_wait_retry--); - if (reg & I40E_PFQF_CTL_1_CLEARFDTABLE_MASK) { - dev_warn(&pf->pdev->dev, "FD table did not flush, needs more time\n"); - } else { - /* replay sideband filters */ - i40e_fdir_filter_restore(pf->vsi[pf->lan_vsi]); - if (!disable_atr) - pf->flags |= I40E_FLAG_FD_ATR_ENABLED; - clear_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state); - if (I40E_DEBUG_FD & pf->hw.debug_mask) - dev_info(&pf->pdev->dev, "FD Filter table flushed and FD-SB replayed.\n"); - } + if (!(time_after(jiffies, min_flush_time)) && + (fd_room < I40E_FDIR_BUFFER_HEAD_ROOM_FOR_ATR)) { + if (I40E_DEBUG_FD & pf->hw.debug_mask) + dev_info(&pf->pdev->dev, "ATR disabled, not enough FD filter space.\n"); + disable_atr = true; + } + + pf->fd_flush_timestamp = jiffies; + pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED; + /* flush all filters */ + wr32(&pf->hw, I40E_PFQF_CTL_1, + I40E_PFQF_CTL_1_CLEARFDTABLE_MASK); + i40e_flush(&pf->hw); + pf->fd_flush_cnt++; + pf->fd_add_err = 0; + do { + /* Check FD flush status every 5-6msec */ + usleep_range(5000, 6000); + reg = rd32(&pf->hw, I40E_PFQF_CTL_1); + if (!(reg & I40E_PFQF_CTL_1_CLEARFDTABLE_MASK)) + break; + } while (flush_wait_retry--); + if (reg & I40E_PFQF_CTL_1_CLEARFDTABLE_MASK) { + dev_warn(&pf->pdev->dev, "FD table did not flush, needs more time\n"); + } else { + /* replay sideband filters */ + i40e_fdir_filter_restore(pf->vsi[pf->lan_vsi]); + if (!disable_atr) + pf->flags |= I40E_FLAG_FD_ATR_ENABLED; + clear_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state); + if (I40E_DEBUG_FD & pf->hw.debug_mask) + dev_info(&pf->pdev->dev, "FD Filter table flushed and FD-SB replayed.\n"); } + } /** -- cgit v0.10.2 From 8ed995ff6bb9c5436db64b3653a0c62a0cd18f08 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Fri, 28 Aug 2015 17:55:57 -0400 Subject: i40evf: use capabilities flags properly Use the capabilities passed to us by the PF driver to control VF driver behavior. In the process, clean up the VLAN add/remove code so it's not a horrible morass of ifdefs. Change-ID: I1050eaf12b658a26fea6813047c9964163c70a73 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 664fde6..1f99930 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -730,6 +730,8 @@ static int i40evf_vlan_rx_add_vid(struct net_device *netdev, { struct i40evf_adapter *adapter = netdev_priv(netdev); + if (!VLAN_ALLOWED(adapter)) + return -EIO; if (i40evf_add_vlan(adapter, vid) == NULL) return -ENOMEM; return 0; @@ -745,8 +747,11 @@ static int i40evf_vlan_rx_kill_vid(struct net_device *netdev, { struct i40evf_adapter *adapter = netdev_priv(netdev); - i40evf_del_vlan(adapter, vid); - return 0; + if (VLAN_ALLOWED(adapter)) { + i40evf_del_vlan(adapter, vid); + return 0; + } + return -EIO; } /** -- cgit v0.10.2 From f578f5f453abf09e8cb4c3aaf1e0c3bdf8125493 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Fri, 28 Aug 2015 17:55:58 -0400 Subject: i40e/i40evf: pass QOS handle to VF The VF really doesn't care about the QOS handle but it will in the future. Since the VF only uses TC0, send it that handle. On the VF side, save the handle and use it to populate the QOS params when we call into the client interface. Change-ID: I76f41b070baeaa09b19383e9168bc677837e0761 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 678623f..ee747dc 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -1210,8 +1210,10 @@ static int i40e_vc_get_vf_resources_msg(struct i40e_vf *vf, u8 *msg) if (vf->lan_vsi_idx) { vfres->vsi_res[i].vsi_id = vf->lan_vsi_id; vfres->vsi_res[i].vsi_type = I40E_VSI_SRIOV; - vfres->vsi_res[i].num_queue_pairs = - pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs; + vfres->vsi_res[i].num_queue_pairs = vsi->alloc_queue_pairs; + /* VFs only use TC 0 */ + vfres->vsi_res[i].qset_handle + = le16_to_cpu(vsi->info.qs_handle[0]); ether_addr_copy(vfres->vsi_res[i].default_mac_addr, vf->default_lan_addr.addr); i++; diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index 27dc3fe..e7a223e 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -66,6 +66,7 @@ struct i40e_vsi { */ u16 rx_itr_setting; u16 tx_itr_setting; + u16 qs_handle; }; /* How many Rx Buffers do we bundle into one write to the hardware ? */ diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 1f99930..c00e495 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -2115,6 +2115,7 @@ int i40evf_process_config(struct i40evf_adapter *adapter) adapter->vsi.tx_itr_setting = (I40E_ITR_DYNAMIC | ITR_REG_TO_USEC(I40E_ITR_TX_DEF)); adapter->vsi.netdev = adapter->netdev; + adapter->vsi.qs_handle = adapter->vsi_res->qset_handle; return 0; } -- cgit v0.10.2 From ce6fcb3f2467902b48e59d1c866c5b4c9f6136eb Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Fri, 28 Aug 2015 17:55:59 -0400 Subject: i40e: print neato new features To help users and developers know what compile options and hardware features are enabled at compile time, print VxLAN is available. Change-ID: I3162f3b7678dc725a597f964217920eb218b480b Signed-off-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index c46d814..a484f22 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -9914,6 +9914,9 @@ static void i40e_print_features(struct i40e_pf *pf) } if (pf->flags & I40E_FLAG_DCB_CAPABLE) buf += sprintf(buf, "DCB "); +#if IS_ENABLED(CONFIG_VXLAN) + buf += sprintf(buf, "VxLAN "); +#endif if (pf->flags & I40E_FLAG_PTP) buf += sprintf(buf, "PTP "); #ifdef I40E_FCOE -- cgit v0.10.2 From 5a7883989b1c57513bb4067ca4b42c96d449d1a5 Mon Sep 17 00:00:00 2001 From: Eli Cohen Date: Thu, 8 Oct 2015 17:13:57 +0300 Subject: net/mlx5_core: Improve mlx5 messages Improve the messages printed by the mlx5 macros to include the device string. In addition, prefix names used by the macros with two underscores to avoid possible name collisions. Signed-off-by: Eli Cohen Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index 566a704..30c0be7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -43,25 +43,25 @@ extern int mlx5_core_debug_mask; -#define mlx5_core_dbg(dev, format, ...) \ - pr_debug("%s:%s:%d:(pid %d): " format, \ - (dev)->priv.name, __func__, __LINE__, current->pid, \ +#define mlx5_core_dbg(__dev, format, ...) \ + dev_dbg(&(__dev)->pdev->dev, "%s:%s:%d:(pid %d): " format, \ + (__dev)->priv.name, __func__, __LINE__, current->pid, \ ##__VA_ARGS__) -#define mlx5_core_dbg_mask(dev, mask, format, ...) \ +#define mlx5_core_dbg_mask(__dev, mask, format, ...) \ do { \ if ((mask) & mlx5_core_debug_mask) \ - mlx5_core_dbg(dev, format, ##__VA_ARGS__); \ + mlx5_core_dbg(__dev, format, ##__VA_ARGS__); \ } while (0) -#define mlx5_core_err(dev, format, ...) \ - pr_err("%s:%s:%d:(pid %d): " format, \ - (dev)->priv.name, __func__, __LINE__, current->pid, \ +#define mlx5_core_err(__dev, format, ...) \ + dev_err(&(__dev)->pdev->dev, "%s:%s:%d:(pid %d): " format, \ + (__dev)->priv.name, __func__, __LINE__, current->pid, \ ##__VA_ARGS__) -#define mlx5_core_warn(dev, format, ...) \ - pr_warn("%s:%s:%d:(pid %d): " format, \ - (dev)->priv.name, __func__, __LINE__, current->pid, \ +#define mlx5_core_warn(__dev, format, ...) \ + dev_warn(&(__dev)->pdev->dev, "%s:%s:%d:(pid %d): " format, \ + (__dev)->priv.name, __func__, __LINE__, current->pid, \ ##__VA_ARGS__) enum { -- cgit v0.10.2 From 020446e01eebc9dbe7eda038e570ab9c7ab13586 Mon Sep 17 00:00:00 2001 From: Eli Cohen Date: Thu, 8 Oct 2015 17:13:58 +0300 Subject: net/mlx5_core: Prepare cmd interface to system errors handling In preparation to handling system errors at the mlx5_core level, change the interface of cmd_work_handler to accept a 64 bit argument for the vector. This allows to encode a flag that signifies when the handler is called as a result of a driver logic that wishes to terminate commands that the hardware may not be able to terminate. Such command completions are detected at the handler and proper return status is encoded. To be able to terminate page handler commands, we make sure to set the corresponding bit in the bitmask. Signed-off-by: Eli Cohen Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index 84838c2..c3e54b7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -254,6 +254,10 @@ static void dump_buf(void *buf, int size, int data_only, int offset) pr_debug("\n"); } +enum { + MLX5_DRIVER_STATUS_ABORTED = 0xfe, +}; + const char *mlx5_command_str(int command) { switch (command) { @@ -473,6 +477,7 @@ static void cmd_work_handler(struct work_struct *work) struct mlx5_core_dev *dev = container_of(cmd, struct mlx5_core_dev, cmd); struct mlx5_cmd_layout *lay; struct semaphore *sem; + unsigned long flags; sem = ent->page_queue ? &cmd->pages_sem : &cmd->sem; down(sem); @@ -485,6 +490,9 @@ static void cmd_work_handler(struct work_struct *work) } } else { ent->idx = cmd->max_reg_cmds; + spin_lock_irqsave(&cmd->alloc_lock, flags); + clear_bit(ent->idx, &cmd->bitmask); + spin_unlock_irqrestore(&cmd->alloc_lock, flags); } ent->token = alloc_token(cmd); @@ -1081,7 +1089,7 @@ static void free_msg(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *msg) } } -void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector) +void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec) { struct mlx5_cmd *cmd = &dev->cmd; struct mlx5_cmd_work_ent *ent; @@ -1092,7 +1100,10 @@ void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector) s64 ds; struct mlx5_cmd_stats *stats; unsigned long flags; + unsigned long vector; + /* there can be at most 32 command queues */ + vector = vec & 0xffffffff; for (i = 0; i < (1 << cmd->log_sz); i++) { if (test_bit(i, &vector)) { struct semaphore *sem; @@ -1110,11 +1121,16 @@ void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector) ent->ret = verify_signature(ent); else ent->ret = 0; - ent->status = ent->lay->status_own >> 1; + if (vec & MLX5_TRIGGERED_CMD_COMP) + ent->status = MLX5_DRIVER_STATUS_ABORTED; + else + ent->status = ent->lay->status_own >> 1; + mlx5_core_dbg(dev, "command completed. ret 0x%x, delivery status %s(0x%x)\n", ent->ret, deliv_status_to_str(ent->status), ent->status); } free_ent(cmd, ent->idx); + if (ent->callback) { ds = ent->ts2 - ent->ts1; if (ent->op < ARRAY_SIZE(cmd->stats)) { diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 8b6d6f2..aa89955 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -731,7 +731,7 @@ void mlx5_eq_pagefault(struct mlx5_core_dev *dev, struct mlx5_eqe *eqe); #endif void mlx5_srq_event(struct mlx5_core_dev *dev, u32 srqn, int event_type); struct mlx5_core_srq *mlx5_core_get_srq(struct mlx5_core_dev *dev, u32 srqn); -void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector); +void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec); void mlx5_cq_event(struct mlx5_core_dev *dev, u32 cqn, int event_type); int mlx5_create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, u8 vecidx, int nent, u64 mask, const char *name, struct mlx5_uar *uar); @@ -865,4 +865,8 @@ static inline int mlx5_get_gid_table_len(u16 param) return 8 * (1 << param); } +enum { + MLX5_TRIGGERED_CMD_COMP = (u64)1 << 32, +}; + #endif /* MLX5_DRIVER_H */ -- cgit v0.10.2 From 0144a95e2ad53a40c62148f44fb0c1f9d2a0d1e9 Mon Sep 17 00:00:00 2001 From: Eli Cohen Date: Thu, 8 Oct 2015 17:13:59 +0300 Subject: net/mlx5_core: Use accessor functions to read from device memory Use ioread function to read health buffer data. In addition, print the firmware version as a string for readability and also use dev_err to have the device string to be printed. Signed-off-by: Eli Cohen Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx5/core/health.c b/drivers/net/ethernet/mellanox/mlx5/core/health.c index 1277c42..8770968 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/health.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/health.c @@ -114,32 +114,41 @@ static const char *hsynd_str(u8 synd) } } -static u16 read_be16(__be16 __iomem *p) +static u16 get_maj(u32 fw) { - return swab16(readl((__force u16 __iomem *) p)); + return fw >> 28; } -static u32 read_be32(__be32 __iomem *p) +static u16 get_min(u32 fw) { - return swab32(readl((__force u32 __iomem *) p)); + return fw >> 16 & 0xfff; +} + +static u16 get_sub(u32 fw) +{ + return fw & 0xffff; } static void print_health_info(struct mlx5_core_dev *dev) { struct mlx5_core_health *health = &dev->priv.health; struct health_buffer __iomem *h = health->health; + char fw_str[18]; + u32 fw; int i; for (i = 0; i < ARRAY_SIZE(h->assert_var); i++) - pr_info("assert_var[%d] 0x%08x\n", i, read_be32(h->assert_var + i)); - - pr_info("assert_exit_ptr 0x%08x\n", read_be32(&h->assert_exit_ptr)); - pr_info("assert_callra 0x%08x\n", read_be32(&h->assert_callra)); - pr_info("fw_ver 0x%08x\n", read_be32(&h->fw_ver)); - pr_info("hw_id 0x%08x\n", read_be32(&h->hw_id)); - pr_info("irisc_index %d\n", readb(&h->irisc_index)); - pr_info("synd 0x%x: %s\n", readb(&h->synd), hsynd_str(readb(&h->synd))); - pr_info("ext_sync 0x%04x\n", read_be16(&h->ext_synd)); + dev_err(&dev->pdev->dev, "assert_var[%d] 0x%08x\n", i, ioread32be(h->assert_var + i)); + + dev_err(&dev->pdev->dev, "assert_exit_ptr 0x%08x\n", ioread32be(&h->assert_exit_ptr)); + dev_err(&dev->pdev->dev, "assert_callra 0x%08x\n", ioread32be(&h->assert_callra)); + fw = ioread32be(&h->fw_ver); + sprintf(fw_str, "%d.%d.%d", get_maj(fw), get_min(fw), get_sub(fw)); + dev_err(&dev->pdev->dev, "fw_ver %s\n", fw_str); + dev_err(&dev->pdev->dev, "hw_id 0x%08x\n", ioread32be(&h->hw_id)); + dev_err(&dev->pdev->dev, "irisc_index %d\n", ioread8(&h->irisc_index)); + dev_err(&dev->pdev->dev, "synd 0x%x: %s\n", ioread8(&h->synd), hsynd_str(ioread8(&h->synd))); + dev_err(&dev->pdev->dev, "ext_synd 0x%04x\n", ioread16be(&h->ext_synd)); } static void poll_health(unsigned long data) -- cgit v0.10.2 From ac6ea6e81a80172612e0c9ef93720f371b198918 Mon Sep 17 00:00:00 2001 From: Eli Cohen Date: Thu, 8 Oct 2015 17:14:00 +0300 Subject: net/mlx5_core: Use private health thread for each device Use a single threaded work queue for each device in the system instead of using one thread for any device. This is required so we can concurrently process system error handling for all the devices that need that. Signed-off-by: Eli Cohen Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx5/core/health.c b/drivers/net/ethernet/mellanox/mlx5/core/health.c index 8770968..9b81e1c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/health.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/health.c @@ -57,31 +57,16 @@ enum { MLX5_HEALTH_SYNDR_HIGH_TEMP = 0x10 }; -static DEFINE_SPINLOCK(health_lock); -static LIST_HEAD(health_list); -static struct work_struct health_work; - static void health_care(struct work_struct *work) { - struct mlx5_core_health *health, *n; + struct mlx5_core_health *health; struct mlx5_core_dev *dev; struct mlx5_priv *priv; - LIST_HEAD(tlist); - - spin_lock_irq(&health_lock); - list_splice_init(&health_list, &tlist); - - spin_unlock_irq(&health_lock); - list_for_each_entry_safe(health, n, &tlist, list) { - priv = container_of(health, struct mlx5_priv, health); - dev = container_of(priv, struct mlx5_core_dev, priv); - mlx5_core_warn(dev, "handling bad device here\n"); - /* nothing yet */ - spin_lock_irq(&health_lock); - list_del_init(&health->list); - spin_unlock_irq(&health_lock); - } + health = container_of(work, struct mlx5_core_health, work); + priv = container_of(health, struct mlx5_priv, health); + dev = container_of(priv, struct mlx5_core_dev, priv); + mlx5_core_warn(dev, "handling bad device here\n"); } static const char *hsynd_str(u8 synd) @@ -168,11 +153,7 @@ static void poll_health(unsigned long data) if (health->miss_counter == MAX_MISSES) { mlx5_core_err(dev, "device's health compromised\n"); print_health_info(dev); - spin_lock_irq(&health_lock); - list_add_tail(&health->list, &health_list); - spin_unlock_irq(&health_lock); - - queue_work(mlx5_core_wq, &health_work); + queue_work(health->wq, &health->work); } else { get_random_bytes(&next, sizeof(next)); next %= HZ; @@ -185,7 +166,6 @@ void mlx5_start_health_poll(struct mlx5_core_dev *dev) { struct mlx5_core_health *health = &dev->priv.health; - INIT_LIST_HEAD(&health->list); init_timer(&health->timer); health->health = &dev->iseg->health; health->health_counter = &dev->iseg->health_counter; @@ -201,18 +181,33 @@ void mlx5_stop_health_poll(struct mlx5_core_dev *dev) struct mlx5_core_health *health = &dev->priv.health; del_timer_sync(&health->timer); - - spin_lock_irq(&health_lock); - if (!list_empty(&health->list)) - list_del_init(&health->list); - spin_unlock_irq(&health_lock); } -void mlx5_health_cleanup(void) +void mlx5_health_cleanup(struct mlx5_core_dev *dev) { + struct mlx5_core_health *health = &dev->priv.health; + + destroy_workqueue(health->wq); } -void __init mlx5_health_init(void) +int mlx5_health_init(struct mlx5_core_dev *dev) { - INIT_WORK(&health_work, health_care); + struct mlx5_core_health *health; + char *name; + + health = &dev->priv.health; + name = kmalloc(64, GFP_KERNEL); + if (!name) + return -ENOMEM; + + strcpy(name, "mlx5_health"); + strcat(name, dev_name(&dev->pdev->dev)); + health->wq = create_singlethread_workqueue(name); + kfree(name); + if (!health->wq) + return -ENOMEM; + + INIT_WORK(&health->work, health_care); + + return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 7718f6a..b6edc58 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -62,7 +62,6 @@ static int prof_sel = MLX5_DEFAULT_PROF; module_param_named(prof_sel, prof_sel, int, 0444); MODULE_PARM_DESC(prof_sel, "profile selector. Valid range 0 - 2"); -struct workqueue_struct *mlx5_core_wq; static LIST_HEAD(intf_list); static LIST_HEAD(dev_list); static DEFINE_MUTEX(intf_mutex); @@ -1046,6 +1045,7 @@ err_pagealloc_cleanup: static int mlx5_unload_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv) { + int err; mlx5_unregister_device(dev); mlx5_cleanup_mr_table(dev); @@ -1060,9 +1060,10 @@ static int mlx5_unload_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv) mlx5_eq_cleanup(dev); mlx5_disable_msix(dev); mlx5_stop_health_poll(dev); - if (mlx5_cmd_teardown_hca(dev)) { + err = mlx5_cmd_teardown_hca(dev); + if (err) { dev_err(&dev->pdev->dev, "tear_down_hca failed, skip cleanup\n"); - return 1; + goto out; } mlx5_pagealloc_stop(dev); mlx5_reclaim_startup_pages(dev); @@ -1070,11 +1071,12 @@ static int mlx5_unload_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv) mlx5_pagealloc_cleanup(dev); mlx5_cmd_cleanup(dev); - return 0; +out: + return err; } static void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event, - unsigned long param) + unsigned long param) { struct mlx5_priv *priv = &dev->priv; struct mlx5_device_context *dev_ctx; @@ -1129,14 +1131,22 @@ static int init_one(struct pci_dev *pdev, goto clean_dev; } + err = mlx5_health_init(dev); + if (err) { + dev_err(&pdev->dev, "mlx5_health_init failed with error code %d\n", err); + goto close_pci; + } + err = mlx5_load_one(dev, priv); if (err) { dev_err(&pdev->dev, "mlx5_load_one failed with error code %d\n", err); - goto close_pci; + goto clean_health; } return 0; +clean_health: + mlx5_health_cleanup(dev); close_pci: mlx5_pci_close(dev, priv); clean_dev: @@ -1153,8 +1163,10 @@ static void remove_one(struct pci_dev *pdev) if (mlx5_unload_one(dev, priv)) { dev_err(&dev->pdev->dev, "mlx5_unload_one failed\n"); + mlx5_health_cleanup(dev); return; } + mlx5_health_cleanup(dev); mlx5_pci_close(dev, priv); pci_set_drvdata(pdev, NULL); kfree(dev); @@ -1184,16 +1196,10 @@ static int __init init(void) int err; mlx5_register_debugfs(); - mlx5_core_wq = create_singlethread_workqueue("mlx5_core_wq"); - if (!mlx5_core_wq) { - err = -ENOMEM; - goto err_debug; - } - mlx5_health_init(); err = pci_register_driver(&mlx5_core_driver); if (err) - goto err_health; + goto err_debug; #ifdef CONFIG_MLX5_CORE_EN mlx5e_init(); @@ -1201,9 +1207,6 @@ static int __init init(void) return 0; -err_health: - mlx5_health_cleanup(); - destroy_workqueue(mlx5_core_wq); err_debug: mlx5_unregister_debugfs(); return err; @@ -1215,8 +1218,6 @@ static void __exit cleanup(void) mlx5e_cleanup(); #endif pci_unregister_driver(&mlx5_core_driver); - mlx5_health_cleanup(); - destroy_workqueue(mlx5_core_wq); mlx5_unregister_debugfs(); } diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index aa89955..41a3287 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -391,9 +391,10 @@ struct mlx5_core_health { struct health_buffer __iomem *health; __be32 __iomem *health_counter; struct timer_list timer; - struct list_head list; u32 prev; int miss_counter; + struct workqueue_struct *wq; + struct work_struct work; }; struct mlx5_cq_table { @@ -676,8 +677,8 @@ int mlx5_alloc_uuars(struct mlx5_core_dev *dev, struct mlx5_uuar_info *uuari); int mlx5_free_uuars(struct mlx5_core_dev *dev, struct mlx5_uuar_info *uuari); int mlx5_alloc_map_uar(struct mlx5_core_dev *mdev, struct mlx5_uar *uar); void mlx5_unmap_free_uar(struct mlx5_core_dev *mdev, struct mlx5_uar *uar); -void mlx5_health_cleanup(void); -void __init mlx5_health_init(void); +void mlx5_health_cleanup(struct mlx5_core_dev *dev); +int mlx5_health_init(struct mlx5_core_dev *dev); void mlx5_start_health_poll(struct mlx5_core_dev *dev); void mlx5_stop_health_poll(struct mlx5_core_dev *dev); int mlx5_buf_alloc_node(struct mlx5_core_dev *dev, int size, -- cgit v0.10.2 From ba4b87aedd7123df4e4cfd1ca4dcfe8a6d756511 Mon Sep 17 00:00:00 2001 From: Ido Shamay Date: Thu, 8 Oct 2015 17:14:01 +0300 Subject: net/mlx4_en: Add steering rules after RSS creation Changed the receive control flow in a way that steering rules are added only when the RSS object is already in RTR/RTS mode. Some optimization features, which are enabled by the device firmware, require this condition in order to be effective. Signed-off-by: Ido Shamay Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 4726122..597d892 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -573,10 +573,8 @@ static int mlx4_en_get_qp(struct mlx4_en_priv *priv) { struct mlx4_en_dev *mdev = priv->mdev; struct mlx4_dev *dev = mdev->dev; - struct mlx4_mac_entry *entry; int index = 0; int err = 0; - u64 reg_id = 0; int *qpn = &priv->base_qpn; u64 mac = mlx4_mac_to_u64(priv->dev->dev_addr); @@ -600,44 +598,11 @@ static int mlx4_en_get_qp(struct mlx4_en_priv *priv) en_dbg(DRV, priv, "Reserved qp %d\n", *qpn); if (err) { en_err(priv, "Failed to reserve qp for mac registration\n"); - goto qp_err; - } - - err = mlx4_en_uc_steer_add(priv, priv->dev->dev_addr, qpn, ®_id); - if (err) - goto steer_err; - - err = mlx4_en_tunnel_steer_add(priv, priv->dev->dev_addr, *qpn, - &priv->tunnel_reg_id); - if (err) - goto tunnel_err; - - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) { - err = -ENOMEM; - goto alloc_err; + mlx4_unregister_mac(dev, priv->port, mac); + return err; } - memcpy(entry->mac, priv->dev->dev_addr, sizeof(entry->mac)); - memcpy(priv->current_mac, entry->mac, sizeof(priv->current_mac)); - entry->reg_id = reg_id; - - hlist_add_head_rcu(&entry->hlist, - &priv->mac_hash[entry->mac[MLX4_EN_MAC_HASH_IDX]]); return 0; - -alloc_err: - if (priv->tunnel_reg_id) - mlx4_flow_detach(priv->mdev->dev, priv->tunnel_reg_id); -tunnel_err: - mlx4_en_uc_steer_release(priv, priv->dev->dev_addr, *qpn, reg_id); - -steer_err: - mlx4_qp_release_range(dev, *qpn, 1); - -qp_err: - mlx4_unregister_mac(dev, priv->port, mac); - return err; } static void mlx4_en_put_qp(struct mlx4_en_priv *priv) @@ -645,39 +610,13 @@ static void mlx4_en_put_qp(struct mlx4_en_priv *priv) struct mlx4_en_dev *mdev = priv->mdev; struct mlx4_dev *dev = mdev->dev; int qpn = priv->base_qpn; - u64 mac; if (dev->caps.steering_mode == MLX4_STEERING_MODE_A0) { - mac = mlx4_mac_to_u64(priv->dev->dev_addr); + u64 mac = mlx4_mac_to_u64(priv->dev->dev_addr); en_dbg(DRV, priv, "Registering MAC: %pM for deleting\n", priv->dev->dev_addr); mlx4_unregister_mac(dev, priv->port, mac); } else { - struct mlx4_mac_entry *entry; - struct hlist_node *tmp; - struct hlist_head *bucket; - unsigned int i; - - for (i = 0; i < MLX4_EN_MAC_HASH_SIZE; ++i) { - bucket = &priv->mac_hash[i]; - hlist_for_each_entry_safe(entry, tmp, bucket, hlist) { - mac = mlx4_mac_to_u64(entry->mac); - en_dbg(DRV, priv, "Registering MAC: %pM for deleting\n", - entry->mac); - mlx4_en_uc_steer_release(priv, entry->mac, - qpn, entry->reg_id); - - mlx4_unregister_mac(dev, priv->port, mac); - hlist_del_rcu(&entry->hlist); - kfree_rcu(entry, rcu); - } - } - - if (priv->tunnel_reg_id) { - mlx4_flow_detach(priv->mdev->dev, priv->tunnel_reg_id); - priv->tunnel_reg_id = 0; - } - en_dbg(DRV, priv, "Releasing qp: port %d, qpn %d\n", priv->port, qpn); mlx4_qp_release_range(dev, qpn, 1); @@ -1283,6 +1222,75 @@ static void mlx4_en_netpoll(struct net_device *dev) } #endif +static int mlx4_en_set_rss_steer_rules(struct mlx4_en_priv *priv) +{ + u64 reg_id; + int err = 0; + int *qpn = &priv->base_qpn; + struct mlx4_mac_entry *entry; + + err = mlx4_en_uc_steer_add(priv, priv->dev->dev_addr, qpn, ®_id); + if (err) + return err; + + err = mlx4_en_tunnel_steer_add(priv, priv->dev->dev_addr, *qpn, + &priv->tunnel_reg_id); + if (err) + goto tunnel_err; + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + err = -ENOMEM; + goto alloc_err; + } + + memcpy(entry->mac, priv->dev->dev_addr, sizeof(entry->mac)); + memcpy(priv->current_mac, entry->mac, sizeof(priv->current_mac)); + entry->reg_id = reg_id; + hlist_add_head_rcu(&entry->hlist, + &priv->mac_hash[entry->mac[MLX4_EN_MAC_HASH_IDX]]); + + return 0; + +alloc_err: + if (priv->tunnel_reg_id) + mlx4_flow_detach(priv->mdev->dev, priv->tunnel_reg_id); + +tunnel_err: + mlx4_en_uc_steer_release(priv, priv->dev->dev_addr, *qpn, reg_id); + return err; +} + +static void mlx4_en_delete_rss_steer_rules(struct mlx4_en_priv *priv) +{ + u64 mac; + unsigned int i; + int qpn = priv->base_qpn; + struct hlist_head *bucket; + struct hlist_node *tmp; + struct mlx4_mac_entry *entry; + + for (i = 0; i < MLX4_EN_MAC_HASH_SIZE; ++i) { + bucket = &priv->mac_hash[i]; + hlist_for_each_entry_safe(entry, tmp, bucket, hlist) { + mac = mlx4_mac_to_u64(entry->mac); + en_dbg(DRV, priv, "Registering MAC:%pM for deleting\n", + entry->mac); + mlx4_en_uc_steer_release(priv, entry->mac, + qpn, entry->reg_id); + + mlx4_unregister_mac(priv->mdev->dev, priv->port, mac); + hlist_del_rcu(&entry->hlist); + kfree_rcu(entry, rcu); + } + } + + if (priv->tunnel_reg_id) { + mlx4_flow_detach(priv->mdev->dev, priv->tunnel_reg_id); + priv->tunnel_reg_id = 0; + } +} + static void mlx4_en_tx_timeout(struct net_device *dev) { struct mlx4_en_priv *priv = netdev_priv(dev); @@ -1684,6 +1692,11 @@ int mlx4_en_start_port(struct net_device *dev) goto tx_err; } + /* Set Unicast and VXLAN steering rules */ + if (mdev->dev->caps.steering_mode != MLX4_STEERING_MODE_A0 && + mlx4_en_set_rss_steer_rules(priv)) + mlx4_warn(mdev, "Failed setting steering rules\n"); + /* Attach rx QP to bradcast address */ eth_broadcast_addr(&mc_list[10]); mc_list[5] = priv->port; /* needed for B0 steering support */ @@ -1831,6 +1844,9 @@ void mlx4_en_stop_port(struct net_device *dev, int detach) for (i = 0; i < priv->tx_ring_num; i++) mlx4_en_free_tx_buf(dev, priv->tx_ring[i]); + if (mdev->dev->caps.steering_mode != MLX4_STEERING_MODE_A0) + mlx4_en_delete_rss_steer_rules(priv); + /* Free RSS qps */ mlx4_en_release_rss_steer(priv); -- cgit v0.10.2 From a5b3c56ef792009f6aff54ba93bd86ceca3e5bfc Mon Sep 17 00:00:00 2001 From: Jack Morgenstein Date: Thu, 8 Oct 2015 17:14:02 +0300 Subject: net/mlx4_core: Fix mailbox leak in error flow when performing update qp The procedure mlx4_update_qp leaks mailboxes in its error-flow, fix that. Signed-off-by: Jack Morgenstein Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c index 2026863..3311f35 100644 --- a/drivers/net/ethernet/mellanox/mlx4/qp.c +++ b/drivers/net/ethernet/mellanox/mlx4/qp.c @@ -422,15 +422,15 @@ int mlx4_update_qp(struct mlx4_dev *dev, u32 qpn, u64 qp_mask = 0; int err = 0; + if (!attr || (attr & ~MLX4_UPDATE_QP_SUPPORTED_ATTRS)) + return -EINVAL; + mailbox = mlx4_alloc_cmd_mailbox(dev); if (IS_ERR(mailbox)) return PTR_ERR(mailbox); cmd = (struct mlx4_update_qp_context *)mailbox->buf; - if (!attr || (attr & ~MLX4_UPDATE_QP_SUPPORTED_ATTRS)) - return -EINVAL; - if (attr & MLX4_UPDATE_QP_SMAC) { pri_addr_path_mask |= 1ULL << MLX4_UPD_QP_PATH_MASK_MAC_INDEX; cmd->qp_context.pri_path.grh_mylmc = params->smac_index; -- cgit v0.10.2 From 95e196337a3bd1a1678e794064d8097600c277de Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Thu, 8 Oct 2015 17:14:03 +0300 Subject: net/mlx4_core: Fix resource tracker error flow in add_res_range The 'for' loop when undoing rb-tree insertions and list-adds in the error flow in add_res_range had errors, fix them. Signed-off-by: Saeed Mahameed Signed-off-by: Jack Morgenstein Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 731423c..ac4b99a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -1238,8 +1238,10 @@ static int add_res_range(struct mlx4_dev *dev, int slave, u64 base, int count, return 0; undo: - for (--i; i >= base; --i) + for (--i; i >= 0; --i) { rb_erase(&res_arr[i]->node, root); + list_del_init(&res_arr[i]->list); + } spin_unlock_irq(mlx4_tlock(dev)); -- cgit v0.10.2 From 61d03535e4be3a46c1e171a25458237e343195e3 Mon Sep 17 00:00:00 2001 From: Yaowei Bai Date: Thu, 8 Oct 2015 21:28:54 +0800 Subject: net/netlink: lockdep_genl_is_held can be boolean This patch makes lockdep_genl_is_held return bool to improve readability due to this particular function only using either one or zero as its return value. No functional change. Signed-off-by: Yaowei Bai Signed-off-by: David S. Miller diff --git a/include/linux/genetlink.h b/include/linux/genetlink.h index 09460d6..a4c61cb 100644 --- a/include/linux/genetlink.h +++ b/include/linux/genetlink.h @@ -8,7 +8,7 @@ extern void genl_lock(void); extern void genl_unlock(void); #ifdef CONFIG_LOCKDEP -extern int lockdep_genl_is_held(void); +extern bool lockdep_genl_is_held(void); #endif /* for synchronisation between af_netlink and genetlink */ diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 75724a9..bc0e504 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -39,7 +39,7 @@ void genl_unlock(void) EXPORT_SYMBOL(genl_unlock); #ifdef CONFIG_LOCKDEP -int lockdep_genl_is_held(void) +bool lockdep_genl_is_held(void) { return lockdep_is_held(&genl_mutex); } -- cgit v0.10.2 From 35498edc6481d588feadee7e76220884d5bbca48 Mon Sep 17 00:00:00 2001 From: Yaowei Bai Date: Thu, 8 Oct 2015 21:28:55 +0800 Subject: net/ieee80211: ieee80211_is_* can be boolean This patch makes ieee80211_is_* return bool to improve readability due to these particular functions only using either one or zero as their return value. No functional change. Signed-off-by: Yaowei Bai Signed-off-by: David S. Miller diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index f79a02a..dcfb2f4 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -121,7 +121,7 @@ #define IEEE80211_MAX_SN IEEE80211_SN_MASK #define IEEE80211_SN_MODULO (IEEE80211_MAX_SN + 1) -static inline int ieee80211_sn_less(u16 sn1, u16 sn2) +static inline bool ieee80211_sn_less(u16 sn1, u16 sn2) { return ((sn1 - sn2) & IEEE80211_SN_MASK) > (IEEE80211_SN_MODULO >> 1); } @@ -250,7 +250,7 @@ struct ieee80211_qos_hdr { * ieee80211_has_tods - check if IEEE80211_FCTL_TODS is set * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_has_tods(__le16 fc) +static inline bool ieee80211_has_tods(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_TODS)) != 0; } @@ -259,7 +259,7 @@ static inline int ieee80211_has_tods(__le16 fc) * ieee80211_has_fromds - check if IEEE80211_FCTL_FROMDS is set * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_has_fromds(__le16 fc) +static inline bool ieee80211_has_fromds(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FROMDS)) != 0; } @@ -268,7 +268,7 @@ static inline int ieee80211_has_fromds(__le16 fc) * ieee80211_has_a4 - check if IEEE80211_FCTL_TODS and IEEE80211_FCTL_FROMDS are set * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_has_a4(__le16 fc) +static inline bool ieee80211_has_a4(__le16 fc) { __le16 tmp = cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS); return (fc & tmp) == tmp; @@ -278,7 +278,7 @@ static inline int ieee80211_has_a4(__le16 fc) * ieee80211_has_morefrags - check if IEEE80211_FCTL_MOREFRAGS is set * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_has_morefrags(__le16 fc) +static inline bool ieee80211_has_morefrags(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_MOREFRAGS)) != 0; } @@ -287,7 +287,7 @@ static inline int ieee80211_has_morefrags(__le16 fc) * ieee80211_has_retry - check if IEEE80211_FCTL_RETRY is set * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_has_retry(__le16 fc) +static inline bool ieee80211_has_retry(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_RETRY)) != 0; } @@ -296,7 +296,7 @@ static inline int ieee80211_has_retry(__le16 fc) * ieee80211_has_pm - check if IEEE80211_FCTL_PM is set * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_has_pm(__le16 fc) +static inline bool ieee80211_has_pm(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_PM)) != 0; } @@ -305,7 +305,7 @@ static inline int ieee80211_has_pm(__le16 fc) * ieee80211_has_moredata - check if IEEE80211_FCTL_MOREDATA is set * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_has_moredata(__le16 fc) +static inline bool ieee80211_has_moredata(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_MOREDATA)) != 0; } @@ -314,7 +314,7 @@ static inline int ieee80211_has_moredata(__le16 fc) * ieee80211_has_protected - check if IEEE80211_FCTL_PROTECTED is set * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_has_protected(__le16 fc) +static inline bool ieee80211_has_protected(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_PROTECTED)) != 0; } @@ -323,7 +323,7 @@ static inline int ieee80211_has_protected(__le16 fc) * ieee80211_has_order - check if IEEE80211_FCTL_ORDER is set * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_has_order(__le16 fc) +static inline bool ieee80211_has_order(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_ORDER)) != 0; } @@ -332,7 +332,7 @@ static inline int ieee80211_has_order(__le16 fc) * ieee80211_is_mgmt - check if type is IEEE80211_FTYPE_MGMT * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_mgmt(__le16 fc) +static inline bool ieee80211_is_mgmt(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT); @@ -342,7 +342,7 @@ static inline int ieee80211_is_mgmt(__le16 fc) * ieee80211_is_ctl - check if type is IEEE80211_FTYPE_CTL * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_ctl(__le16 fc) +static inline bool ieee80211_is_ctl(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE)) == cpu_to_le16(IEEE80211_FTYPE_CTL); @@ -352,7 +352,7 @@ static inline int ieee80211_is_ctl(__le16 fc) * ieee80211_is_data - check if type is IEEE80211_FTYPE_DATA * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_data(__le16 fc) +static inline bool ieee80211_is_data(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE)) == cpu_to_le16(IEEE80211_FTYPE_DATA); @@ -362,7 +362,7 @@ static inline int ieee80211_is_data(__le16 fc) * ieee80211_is_data_qos - check if type is IEEE80211_FTYPE_DATA and IEEE80211_STYPE_QOS_DATA is set * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_data_qos(__le16 fc) +static inline bool ieee80211_is_data_qos(__le16 fc) { /* * mask with QOS_DATA rather than IEEE80211_FCTL_STYPE as we just need @@ -376,7 +376,7 @@ static inline int ieee80211_is_data_qos(__le16 fc) * ieee80211_is_data_present - check if type is IEEE80211_FTYPE_DATA and has data * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_data_present(__le16 fc) +static inline bool ieee80211_is_data_present(__le16 fc) { /* * mask with 0x40 and test that that bit is clear to only return true @@ -390,7 +390,7 @@ static inline int ieee80211_is_data_present(__le16 fc) * ieee80211_is_assoc_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ASSOC_REQ * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_assoc_req(__le16 fc) +static inline bool ieee80211_is_assoc_req(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_REQ); @@ -400,7 +400,7 @@ static inline int ieee80211_is_assoc_req(__le16 fc) * ieee80211_is_assoc_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ASSOC_RESP * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_assoc_resp(__le16 fc) +static inline bool ieee80211_is_assoc_resp(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_RESP); @@ -410,7 +410,7 @@ static inline int ieee80211_is_assoc_resp(__le16 fc) * ieee80211_is_reassoc_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_REASSOC_REQ * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_reassoc_req(__le16 fc) +static inline bool ieee80211_is_reassoc_req(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_REQ); @@ -420,7 +420,7 @@ static inline int ieee80211_is_reassoc_req(__le16 fc) * ieee80211_is_reassoc_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_REASSOC_RESP * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_reassoc_resp(__le16 fc) +static inline bool ieee80211_is_reassoc_resp(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_RESP); @@ -430,7 +430,7 @@ static inline int ieee80211_is_reassoc_resp(__le16 fc) * ieee80211_is_probe_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_PROBE_REQ * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_probe_req(__le16 fc) +static inline bool ieee80211_is_probe_req(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ); @@ -440,7 +440,7 @@ static inline int ieee80211_is_probe_req(__le16 fc) * ieee80211_is_probe_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_PROBE_RESP * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_probe_resp(__le16 fc) +static inline bool ieee80211_is_probe_resp(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP); @@ -450,7 +450,7 @@ static inline int ieee80211_is_probe_resp(__le16 fc) * ieee80211_is_beacon - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_BEACON * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_beacon(__le16 fc) +static inline bool ieee80211_is_beacon(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON); @@ -460,7 +460,7 @@ static inline int ieee80211_is_beacon(__le16 fc) * ieee80211_is_atim - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ATIM * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_atim(__le16 fc) +static inline bool ieee80211_is_atim(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ATIM); @@ -470,7 +470,7 @@ static inline int ieee80211_is_atim(__le16 fc) * ieee80211_is_disassoc - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_DISASSOC * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_disassoc(__le16 fc) +static inline bool ieee80211_is_disassoc(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DISASSOC); @@ -480,7 +480,7 @@ static inline int ieee80211_is_disassoc(__le16 fc) * ieee80211_is_auth - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_AUTH * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_auth(__le16 fc) +static inline bool ieee80211_is_auth(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH); @@ -490,7 +490,7 @@ static inline int ieee80211_is_auth(__le16 fc) * ieee80211_is_deauth - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_DEAUTH * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_deauth(__le16 fc) +static inline bool ieee80211_is_deauth(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH); @@ -500,7 +500,7 @@ static inline int ieee80211_is_deauth(__le16 fc) * ieee80211_is_action - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ACTION * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_action(__le16 fc) +static inline bool ieee80211_is_action(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); @@ -510,7 +510,7 @@ static inline int ieee80211_is_action(__le16 fc) * ieee80211_is_back_req - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_BACK_REQ * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_back_req(__le16 fc) +static inline bool ieee80211_is_back_req(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK_REQ); @@ -520,7 +520,7 @@ static inline int ieee80211_is_back_req(__le16 fc) * ieee80211_is_back - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_BACK * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_back(__le16 fc) +static inline bool ieee80211_is_back(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK); @@ -530,7 +530,7 @@ static inline int ieee80211_is_back(__le16 fc) * ieee80211_is_pspoll - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_PSPOLL * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_pspoll(__le16 fc) +static inline bool ieee80211_is_pspoll(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL); @@ -540,7 +540,7 @@ static inline int ieee80211_is_pspoll(__le16 fc) * ieee80211_is_rts - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_RTS * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_rts(__le16 fc) +static inline bool ieee80211_is_rts(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS); @@ -550,7 +550,7 @@ static inline int ieee80211_is_rts(__le16 fc) * ieee80211_is_cts - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CTS * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_cts(__le16 fc) +static inline bool ieee80211_is_cts(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS); @@ -560,7 +560,7 @@ static inline int ieee80211_is_cts(__le16 fc) * ieee80211_is_ack - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_ACK * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_ack(__le16 fc) +static inline bool ieee80211_is_ack(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_ACK); @@ -570,7 +570,7 @@ static inline int ieee80211_is_ack(__le16 fc) * ieee80211_is_cfend - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CFEND * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_cfend(__le16 fc) +static inline bool ieee80211_is_cfend(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CFEND); @@ -580,7 +580,7 @@ static inline int ieee80211_is_cfend(__le16 fc) * ieee80211_is_cfendack - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CFENDACK * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_cfendack(__le16 fc) +static inline bool ieee80211_is_cfendack(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CFENDACK); @@ -590,7 +590,7 @@ static inline int ieee80211_is_cfendack(__le16 fc) * ieee80211_is_nullfunc - check if frame is a regular (non-QoS) nullfunc frame * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_nullfunc(__le16 fc) +static inline bool ieee80211_is_nullfunc(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC); @@ -600,7 +600,7 @@ static inline int ieee80211_is_nullfunc(__le16 fc) * ieee80211_is_qos_nullfunc - check if frame is a QoS nullfunc frame * @fc: frame control bytes in little-endian byteorder */ -static inline int ieee80211_is_qos_nullfunc(__le16 fc) +static inline bool ieee80211_is_qos_nullfunc(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_NULLFUNC); @@ -624,7 +624,7 @@ static inline bool ieee80211_is_bufferable_mmpdu(__le16 fc) * ieee80211_is_first_frag - check if IEEE80211_SCTL_FRAG is not set * @seq_ctrl: frame sequence control bytes in little-endian byteorder */ -static inline int ieee80211_is_first_frag(__le16 seq_ctrl) +static inline bool ieee80211_is_first_frag(__le16 seq_ctrl) { return (seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG)) == 0; } -- cgit v0.10.2 From 875e08294911b3cb8c60416d64d990809421de29 Mon Sep 17 00:00:00 2001 From: Yaowei Bai Date: Thu, 8 Oct 2015 21:28:56 +0800 Subject: net/nfnetlink: lockdep_nfnl_is_held can be boolean This patch makes lockdep_nfnl_is_held return bool to improve readability due to this particular function only using either one or zero as its return value. No functional change. Signed-off-by: Yaowei Bai Signed-off-by: David S. Miller diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h index e955d47..249d1bb 100644 --- a/include/linux/netfilter/nfnetlink.h +++ b/include/linux/netfilter/nfnetlink.h @@ -45,11 +45,11 @@ int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u32 portid, void nfnl_lock(__u8 subsys_id); void nfnl_unlock(__u8 subsys_id); #ifdef CONFIG_PROVE_LOCKING -int lockdep_nfnl_is_held(__u8 subsys_id); +bool lockdep_nfnl_is_held(__u8 subsys_id); #else -static inline int lockdep_nfnl_is_held(__u8 subsys_id) +static inline bool lockdep_nfnl_is_held(__u8 subsys_id) { - return 1; + return true; } #endif /* CONFIG_PROVE_LOCKING */ diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index 70277b1..f1d9e88 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c @@ -64,7 +64,7 @@ void nfnl_unlock(__u8 subsys_id) EXPORT_SYMBOL_GPL(nfnl_unlock); #ifdef CONFIG_PROVE_LOCKING -int lockdep_nfnl_is_held(u8 subsys_id) +bool lockdep_nfnl_is_held(u8 subsys_id) { return lockdep_is_held(&table[subsys_id].mutex); } -- cgit v0.10.2 From d6fbaea5f635216c9861587c4e658086cf3b1b6b Mon Sep 17 00:00:00 2001 From: Yaowei Bai Date: Thu, 8 Oct 2015 21:28:57 +0800 Subject: net/can: can_dropped_invalid_skb can be boolean This patch makes can_dropped_invalid_skb return bool due to this particular function only using either one or zero as its return value. No functional change. Signed-off-by: Yaowei Bai Acked-by: Marc Kleine-Budde Signed-off-by: David S. Miller diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h index 56dcadd..735f9f8 100644 --- a/include/linux/can/dev.h +++ b/include/linux/can/dev.h @@ -78,7 +78,7 @@ struct can_priv { #define get_canfd_dlc(i) (min_t(__u8, (i), CANFD_MAX_DLC)) /* Drop a given socketbuffer if it does not contain a valid CAN frame. */ -static inline int can_dropped_invalid_skb(struct net_device *dev, +static inline bool can_dropped_invalid_skb(struct net_device *dev, struct sk_buff *skb) { const struct canfd_frame *cfd = (struct canfd_frame *)skb->data; @@ -94,12 +94,12 @@ static inline int can_dropped_invalid_skb(struct net_device *dev, } else goto inval_skb; - return 0; + return false; inval_skb: kfree_skb(skb); dev->stats.tx_dropped++; - return 1; + return true; } static inline bool can_is_canfd_skb(const struct sk_buff *skb) -- cgit v0.10.2 From 0c6119d99bf5df9403a688d267537284e9cc8bcb Mon Sep 17 00:00:00 2001 From: Yaowei Bai Date: Thu, 8 Oct 2015 21:28:58 +0800 Subject: net/dccp: dccp_list_has_service can be boolean This patch makes dccp_list_has_service return bool due to this particular function only using either one or zero as its return value. No functional change. Signed-off-by: Yaowei Bai Signed-off-by: David S. Miller diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 2210254..61d042b 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -202,16 +202,16 @@ struct dccp_service_list { #define DCCP_SERVICE_INVALID_VALUE htonl((__u32)-1) #define DCCP_SERVICE_CODE_IS_ABSENT 0 -static inline int dccp_list_has_service(const struct dccp_service_list *sl, +static inline bool dccp_list_has_service(const struct dccp_service_list *sl, const __be32 service) { if (likely(sl != NULL)) { u32 i = sl->dccpsl_nr; while (i--) if (sl->dccpsl_list[i] == service) - return 1; + return true; } - return 0; + return false; } struct dccp_ackvec; -- cgit v0.10.2 From 45ae74f56162e7a017c3a4e130cf1bcd8d2d17cc Mon Sep 17 00:00:00 2001 From: Yaowei Bai Date: Thu, 8 Oct 2015 21:28:59 +0800 Subject: net/dccp: dccp_bad_service_code can be boolean This patch makes dccp_bad_service_code return bool due to these particular functions only using either one or zero as their return value. dccp_list_has_service is also been made return bool in this patchset. No functional change. Signed-off-by: Yaowei Bai Signed-off-by: David S. Miller diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index e1f8234..923f5a1 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -325,13 +325,13 @@ void dccp_send_close(struct sock *sk, const int active); int dccp_invalid_packet(struct sk_buff *skb); u32 dccp_sample_rtt(struct sock *sk, long delta); -static inline int dccp_bad_service_code(const struct sock *sk, +static inline bool dccp_bad_service_code(const struct sock *sk, const __be32 service) { const struct dccp_sock *dp = dccp_sk(sk); if (dp->dccps_service == service) - return 0; + return false; return !dccp_list_has_service(dp->dccps_service_list, service); } -- cgit v0.10.2 From c3225164cf60ccecce2459dcb5813dd798233f2d Mon Sep 17 00:00:00 2001 From: Yaowei Bai Date: Thu, 8 Oct 2015 21:29:00 +0800 Subject: net/inetdevice: inet_ifa_match can be boolean This patch makes inet_ifa_match return bool due to this particular function only using either one or zero as its return value. No functional change. Signed-off-by: Yaowei Bai Signed-off-by: David S. Miller diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index a4328ce..3b0999e 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -171,7 +171,7 @@ __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev, __be32 dst, __be32 local, int scope); struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix, __be32 mask); -static __inline__ int inet_ifa_match(__be32 addr, struct in_ifaddr *ifa) +static __inline__ bool inet_ifa_match(__be32 addr, struct in_ifaddr *ifa) { return !((addr^ifa->ifa_address)&ifa->ifa_mask); } -- cgit v0.10.2 From f06cc7b284f3dfb2c5decbf9fde711b50a530050 Mon Sep 17 00:00:00 2001 From: Yaowei Bai Date: Thu, 8 Oct 2015 21:29:01 +0800 Subject: net/inetdevice: bad_mask can be boolean This patch makes bad_mask return bool due to this particular function only using either one or zero as its return value. No functional change. Signed-off-by: Yaowei Bai Signed-off-by: David S. Miller diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index 3b0999e..ee971f3 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -180,15 +180,15 @@ static __inline__ bool inet_ifa_match(__be32 addr, struct in_ifaddr *ifa) * Check if a mask is acceptable. */ -static __inline__ int bad_mask(__be32 mask, __be32 addr) +static __inline__ bool bad_mask(__be32 mask, __be32 addr) { __u32 hmask; if (addr & (mask = ~mask)) - return 1; + return true; hmask = ntohl(mask); if (hmask & (hmask+1)) - return 1; - return 0; + return true; + return false; } #define for_primary_ifa(in_dev) { struct in_ifaddr *ifa; \ -- cgit v0.10.2 From 0cbf334376d5e82d7a2f5cd234ca4f5d0843f3ea Mon Sep 17 00:00:00 2001 From: Yaowei Bai Date: Thu, 8 Oct 2015 21:29:02 +0800 Subject: net/core: lockdep_rtnl_is_held can be boolean This patch makes lockdep_rtnl_is_held return bool due to this particular function only using either one or zero as its return value. In another patch lockdep_is_held is also made return bool. No functional change. Signed-off-by: Yaowei Bai Signed-off-by: David S. Miller diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index 39adaa9..4be5048 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -33,11 +33,11 @@ extern wait_queue_head_t netdev_unregistering_wq; extern struct mutex net_mutex; #ifdef CONFIG_PROVE_LOCKING -extern int lockdep_rtnl_is_held(void); +extern bool lockdep_rtnl_is_held(void); #else -static inline int lockdep_rtnl_is_held(void) +static inline bool lockdep_rtnl_is_held(void) { - return 1; + return true; } #endif /* #ifdef CONFIG_PROVE_LOCKING */ diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index b2258a3..2477595 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -96,7 +96,7 @@ int rtnl_is_locked(void) EXPORT_SYMBOL(rtnl_is_locked); #ifdef CONFIG_PROVE_LOCKING -int lockdep_rtnl_is_held(void) +bool lockdep_rtnl_is_held(void) { return lockdep_is_held(&rtnl_mutex); } -- cgit v0.10.2 From b6191aeeec1045decb5964e6b5e8c314f5982c85 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 7 Oct 2015 17:27:43 -0400 Subject: net/core: make sock_diag.c explicitly non-modular The Makefile currently controlling compilation of this code lists it under "obj-y" ...meaning that it currently is not being built as a module by anyone. Lets remove the modular code that is essentially orphaned, 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. We can change to one of the other priority initcalls (subsys?) at any later date, if desired. We can't remove module.h since the file uses other module related stuff even though it is not modular itself. We move the information from the MODULE_LICENSE tag to the top of the file, since that information is not captured anywhere else. The MODULE_ALIAS_NET_PF_PROTO becomes a no-op in the non modular case, so it is removed. Cc: "David S. Miller" Cc: Eric Dumazet Cc: Nicolas Dichtel Cc: Daniel Borkmann Cc: Alexei Starovoitov Cc: Craig Gallek Cc: netdev@vger.kernel.org Signed-off-by: Paul Gortmaker Signed-off-by: David S. Miller diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c index 817622f..0c1d58d 100644 --- a/net/core/sock_diag.c +++ b/net/core/sock_diag.c @@ -1,3 +1,5 @@ +/* License: GPL */ + #include #include #include @@ -323,14 +325,4 @@ static int __init sock_diag_init(void) BUG_ON(!broadcast_wq); return register_pernet_subsys(&diag_net_ops); } - -static void __exit sock_diag_exit(void) -{ - unregister_pernet_subsys(&diag_net_ops); - destroy_workqueue(broadcast_wq); -} - -module_init(sock_diag_init); -module_exit(sock_diag_exit); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_SOCK_DIAG); +device_initcall(sock_diag_init); -- cgit v0.10.2 From 36b9ad8084bd7ecf6d2241beca23e71f5f4b0cf1 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 7 Oct 2015 17:27:44 -0400 Subject: net/dcb: make dcbnl.c explicitly non-modular The Kconfig currently controlling compilation of this code is: net/dcb/Kconfig:config DCB net/dcb/Kconfig: bool "Data Center Bridging support" ...meaning that it currently is not being built as a module by anyone. Lets remove the modular code that is essentially orphaned, 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. We can change to one of the other priority initcalls (subsys?) at any later date, if desired. We also delete the MODULE_LICENSE tag etc. since all that information is (or is now) already contained at the top of the file in the comments. Cc: "David S. Miller" Cc: Or Gerlitz Cc: Anish Bhatt Cc: John Fastabend Cc: Shani Michaeli Cc: netdev@vger.kernel.org Signed-off-by: Paul Gortmaker Signed-off-by: David S. Miller diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index 5b21f6f..4f6c186 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License along with * this program; if not, see . * + * Description: Data Center Bridging netlink interface * Author: Lucy Liu */ @@ -24,7 +25,7 @@ #include #include #include -#include +#include #include /* Data Center Bridging (DCB) is a collection of Ethernet enhancements @@ -48,10 +49,6 @@ * features for capable devices. */ -MODULE_AUTHOR("Lucy Liu, "); -MODULE_DESCRIPTION("Data Center Bridging netlink interface"); -MODULE_LICENSE("GPL"); - /**************** DCB attribute policies *************************************/ /* DCB netlink attributes policy */ @@ -1935,19 +1932,6 @@ int dcb_ieee_delapp(struct net_device *dev, struct dcb_app *del) } EXPORT_SYMBOL(dcb_ieee_delapp); -static void dcb_flushapp(void) -{ - struct dcb_app_type *app; - struct dcb_app_type *tmp; - - spin_lock_bh(&dcb_lock); - list_for_each_entry_safe(app, tmp, &dcb_app_list, list) { - list_del(&app->list); - kfree(app); - } - spin_unlock_bh(&dcb_lock); -} - static int __init dcbnl_init(void) { INIT_LIST_HEAD(&dcb_app_list); @@ -1957,12 +1941,4 @@ static int __init dcbnl_init(void) return 0; } -module_init(dcbnl_init); - -static void __exit dcbnl_exit(void) -{ - rtnl_unregister(PF_UNSPEC, RTM_GETDCB); - rtnl_unregister(PF_UNSPEC, RTM_SETDCB); - dcb_flushapp(); -} -module_exit(dcbnl_exit); +device_initcall(dcbnl_init); -- cgit v0.10.2 From 075640e364f3b46311766f0eff28bd3695637e16 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 7 Oct 2015 17:27:45 -0400 Subject: net/sched: make sch_blackhole.c explicitly non-modular The Kconfig currently controlling compilation of this code is: net/sched/Kconfig:menuconfig NET_SCHED net/sched/Kconfig: bool "QoS and/or fair queueing" ...meaning that it currently is not being built as a module by anyone. Lets remove the modular code that is essentially orphaned, 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. We can change to one of the other priority initcalls (subsys?) at any later date, if desired. We also delete the MODULE_LICENSE tag since all that information is already contained at the top of the file in the comments. Cc: Jamal Hadi Salim Cc: "David S. Miller" Cc: netdev@vger.kernel.org Signed-off-by: Paul Gortmaker Signed-off-by: David S. Miller diff --git a/net/sched/sch_blackhole.c b/net/sched/sch_blackhole.c index 094a874..3fee70d 100644 --- a/net/sched/sch_blackhole.c +++ b/net/sched/sch_blackhole.c @@ -11,7 +11,7 @@ * Note: Quantum tunneling is not supported. */ -#include +#include #include #include #include @@ -37,17 +37,8 @@ static struct Qdisc_ops blackhole_qdisc_ops __read_mostly = { .owner = THIS_MODULE, }; -static int __init blackhole_module_init(void) +static int __init blackhole_init(void) { return register_qdisc(&blackhole_qdisc_ops); } - -static void __exit blackhole_module_exit(void) -{ - unregister_qdisc(&blackhole_qdisc_ops); -} - -module_init(blackhole_module_init) -module_exit(blackhole_module_exit) - -MODULE_LICENSE("GPL"); +device_initcall(blackhole_init) -- cgit v0.10.2 From b3c8ec35a93d61fde15eefde2d54ecf2f5950f68 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 7 Oct 2015 17:27:46 -0400 Subject: drivers/net/ethernet: make ti/cpsw-phy-sel.c explicitly non-modular The Kconfig currently controlling compilation of this code is: drivers/net/ethernet/ti/Kconfig:config TI_CPSW_PHY_SEL drivers/net/ethernet/ti/Kconfig: bool "TI CPSW Switch Phy sel Support" ...meaning that it currently is not being built as a module by anyone. Lets remove the couple traces of modularity so that when reading the driver there is no doubt it is builtin-only. Since module_platform_driver() uses the same init level priority as builtin_platform_driver() the init ordering remains unchanged with this commit. Also note that MODULE_DEVICE_TABLE is a no-op for non-modular code. We also delete the MODULE_LICENSE tag etc. since all that information was (or is now) contained at the top of the file in the comments. Cc: "David S. Miller" Cc: Varka Bhadram Cc: netdev@vger.kernel.org Signed-off-by: Paul Gortmaker Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/ti/cpsw-phy-sel.c b/drivers/net/ethernet/ti/cpsw-phy-sel.c index 0ea78326..e9cc61e 100644 --- a/drivers/net/ethernet/ti/cpsw-phy-sel.c +++ b/drivers/net/ethernet/ti/cpsw-phy-sel.c @@ -2,6 +2,8 @@ * * Copyright (C) 2013 Texas Instruments * + * Module Author: Mugunthan V N + * * 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. @@ -13,7 +15,7 @@ */ #include -#include +#include #include #include #include @@ -173,7 +175,6 @@ static const struct of_device_id cpsw_phy_sel_id_table[] = { }, {} }; -MODULE_DEVICE_TABLE(of, cpsw_phy_sel_id_table); static int cpsw_phy_sel_probe(struct platform_device *pdev) { @@ -214,7 +215,4 @@ static struct platform_driver cpsw_phy_sel_driver = { .of_match_table = cpsw_phy_sel_id_table, }, }; - -module_platform_driver(cpsw_phy_sel_driver); -MODULE_AUTHOR("Mugunthan V N "); -MODULE_LICENSE("GPL v2"); +builtin_platform_driver(cpsw_phy_sel_driver); -- cgit v0.10.2 From ff936a04e5f28b7e0455be0e7fa91334f89e4b44 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Wed, 7 Oct 2015 10:55:41 -0700 Subject: bpf: fix cb access in socket filter programs eBPF socket filter programs may see junk in 'u32 cb[5]' area, since it could have been used by protocol layers earlier. For socket filter programs used in af_packet we need to clean 20 bytes of skb->cb area if it could be used by the program. For programs attached to TCP/UDP sockets we need to save/restore these 20 bytes, since it's used by protocol layers. Remove SK_RUN_FILTER macro, since it's no longer used. Long term we may move this bpf cb area to per-cpu scratch, but that requires addition of new 'per-cpu load/store' instructions, so not suitable as a short term fix. Fixes: d691f9e8d440 ("bpf: allow programs to write to certain skb fields") Reported-by: Eric Dumazet Signed-off-by: Alexei Starovoitov Signed-off-by: David S. Miller diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 3697ad5..b4fdee6 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -100,6 +100,8 @@ enum bpf_access_type { BPF_WRITE = 2 }; +struct bpf_prog; + struct bpf_verifier_ops { /* return eBPF function prototype for verification */ const struct bpf_func_proto *(*get_func_proto)(enum bpf_func_id func_id); @@ -111,7 +113,7 @@ struct bpf_verifier_ops { u32 (*convert_ctx_access)(enum bpf_access_type type, int dst_reg, int src_reg, int ctx_off, - struct bpf_insn *insn); + struct bpf_insn *insn, struct bpf_prog *prog); }; struct bpf_prog_type_list { @@ -120,8 +122,6 @@ struct bpf_prog_type_list { enum bpf_prog_type type; }; -struct bpf_prog; - struct bpf_prog_aux { atomic_t refcnt; u32 used_map_cnt; diff --git a/include/linux/filter.h b/include/linux/filter.h index 1bbce14..4165e9a 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -302,10 +303,6 @@ struct bpf_prog_aux; bpf_size; \ }) -/* Macro to invoke filter function. */ -#define SK_RUN_FILTER(filter, ctx) \ - (*filter->prog->bpf_func)(ctx, filter->prog->insnsi) - #ifdef CONFIG_COMPAT /* A struct sock_filter is architecture independent. */ struct compat_sock_fprog { @@ -329,6 +326,7 @@ struct bpf_prog { kmemcheck_bitfield_begin(meta); 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? */ kmemcheck_bitfield_end(meta); u32 len; /* Number of filter blocks */ @@ -352,6 +350,39 @@ struct sk_filter { #define BPF_PROG_RUN(filter, ctx) (*filter->bpf_func)(ctx, filter->insnsi) +static inline u32 bpf_prog_run_save_cb(const struct bpf_prog *prog, + struct sk_buff *skb) +{ + u8 *cb_data = qdisc_skb_cb(skb)->data; + u8 saved_cb[QDISC_CB_PRIV_LEN]; + u32 res; + + BUILD_BUG_ON(FIELD_SIZEOF(struct __sk_buff, cb) != + QDISC_CB_PRIV_LEN); + + if (unlikely(prog->cb_access)) { + memcpy(saved_cb, cb_data, sizeof(saved_cb)); + memset(cb_data, 0, sizeof(saved_cb)); + } + + res = BPF_PROG_RUN(prog, skb); + + if (unlikely(prog->cb_access)) + memcpy(cb_data, saved_cb, sizeof(saved_cb)); + + return res; +} + +static inline u32 bpf_prog_run_clear_cb(const struct bpf_prog *prog, + struct sk_buff *skb) +{ + u8 *cb_data = qdisc_skb_cb(skb)->data; + + if (unlikely(prog->cb_access)) + memset(cb_data, 0, QDISC_CB_PRIV_LEN); + return BPF_PROG_RUN(prog, skb); +} + static inline unsigned int bpf_prog_size(unsigned int proglen) { return max(sizeof(struct bpf_prog), diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index b074b23..f8da034 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -2024,7 +2024,7 @@ static int convert_ctx_accesses(struct verifier_env *env) cnt = env->prog->aux->ops-> convert_ctx_access(type, insn->dst_reg, insn->src_reg, - insn->off, insn_buf); + insn->off, insn_buf, env->prog); if (cnt == 0 || cnt >= ARRAY_SIZE(insn_buf)) { verbose("bpf verifier is misconfigured\n"); return -EINVAL; diff --git a/net/core/filter.c b/net/core/filter.c index 342e6c8..5f4cf1c 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -56,10 +56,10 @@ * @sk: sock associated with &sk_buff * @skb: buffer to filter * - * Run the filter code and then cut skb->data to correct size returned by - * SK_RUN_FILTER. If pkt_len is 0 we toss packet. If skb->len is smaller + * Run the eBPF program and then cut skb->data to correct size returned by + * the program. If pkt_len is 0 we toss packet. If skb->len is smaller * than pkt_len we keep whole skb->data. This is the socket level - * wrapper to SK_RUN_FILTER. It returns 0 if the packet should + * wrapper to BPF_PROG_RUN. It returns 0 if the packet should * be accepted or -EPERM if the packet should be tossed. * */ @@ -83,7 +83,7 @@ int sk_filter(struct sock *sk, struct sk_buff *skb) rcu_read_lock(); filter = rcu_dereference(sk->sk_filter); if (filter) { - unsigned int pkt_len = SK_RUN_FILTER(filter, skb); + unsigned int pkt_len = bpf_prog_run_save_cb(filter->prog, skb); err = pkt_len ? pskb_trim(skb, pkt_len) : -EPERM; } @@ -1736,7 +1736,8 @@ static bool tc_cls_act_is_valid_access(int off, int size, static u32 bpf_net_convert_ctx_access(enum bpf_access_type type, int dst_reg, int src_reg, int ctx_off, - struct bpf_insn *insn_buf) + struct bpf_insn *insn_buf, + struct bpf_prog *prog) { struct bpf_insn *insn = insn_buf; @@ -1827,6 +1828,7 @@ static u32 bpf_net_convert_ctx_access(enum bpf_access_type type, int dst_reg, offsetof(struct __sk_buff, cb[4]): BUILD_BUG_ON(FIELD_SIZEOF(struct qdisc_skb_cb, data) < 20); + prog->cb_access = 1; ctx_off -= offsetof(struct __sk_buff, cb[0]); ctx_off += offsetof(struct sk_buff, cb); ctx_off += offsetof(struct qdisc_skb_cb, data); diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 81c900f..104910f 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1423,7 +1423,7 @@ static unsigned int fanout_demux_bpf(struct packet_fanout *f, rcu_read_lock(); prog = rcu_dereference(f->bpf_prog); if (prog) - ret = BPF_PROG_RUN(prog, skb) % num; + ret = bpf_prog_run_clear_cb(prog, skb) % num; rcu_read_unlock(); return ret; @@ -1939,16 +1939,16 @@ out_free: return err; } -static unsigned int run_filter(const struct sk_buff *skb, - const struct sock *sk, - unsigned int res) +static unsigned int run_filter(struct sk_buff *skb, + const struct sock *sk, + unsigned int res) { struct sk_filter *filter; rcu_read_lock(); filter = rcu_dereference(sk->sk_filter); if (filter != NULL) - res = SK_RUN_FILTER(filter, skb); + res = bpf_prog_run_clear_cb(filter->prog, skb); rcu_read_unlock(); return res; -- cgit v0.10.2 From 21d11bd6f0f15c9c782d1f09557bb71f582a3f53 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Thu, 8 Oct 2015 10:08:23 +0530 Subject: cxgb4: Enhance driver to update FW, when FW is too old t4_check_fw_version() can return several error codes (-EINVAL, -EBUSY, -EAGAIN). The present code sets the adapter state to UNINIT only if its an EFAULT. In all the error cases set the adapter to uninitialized state. In t4_check_fw_version() if call to t4_get_fw_version() fails, repeat the operation a few times before returning failure. Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 9f1f5b2..c29227e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -3698,7 +3698,7 @@ static int adap_init0(struct adapter *adap) t4_get_tp_version(adap, &adap->params.tp_vers); ret = t4_check_fw_version(adap); /* If firmware is too old (not supported by driver) force an update. */ - if (ret == -EFAULT) + if (ret) state = DEV_STATE_UNINIT; if ((adap->flags & MASTER_PF) && state != DEV_STATE_INIT) { struct fw_info *fw_info; diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index dc6ce31..cf61a58 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -2981,11 +2981,15 @@ int t4_get_exprom_version(struct adapter *adap, u32 *vers) */ int t4_check_fw_version(struct adapter *adap) { - int ret, major, minor, micro; + int i, ret, major, minor, micro; int exp_major, exp_minor, exp_micro; unsigned int chip_version = CHELSIO_CHIP_VERSION(adap->params.chip); ret = t4_get_fw_version(adap, &adap->params.fw_vers); + /* Try multiple times before returning error */ + for (i = 0; (ret == -EBUSY || ret == -EAGAIN) && i < 3; i++) + ret = t4_get_fw_version(adap, &adap->params.fw_vers); + if (ret) return ret; -- cgit v0.10.2 From e446f9dfe17bbaa76a1fe22912636f38be1e1af8 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 8 Oct 2015 05:01:55 -0700 Subject: net: synack packets can be attached to request sockets selinux needs few changes to accommodate fact that SYNACK messages can be attached to a request socket, lacking sk_security pointer (Only syncookies are still attached to a TCP_LISTEN socket) Adds a new sk_listener() helper, and use it in selinux and sch_fq Fixes: ca6fb0651883 ("tcp: attach SYNACK messages to request sockets instead of listener") Signed-off-by: Eric Dumazet Reported by: kernel test robot Cc: Paul Moore Cc: Stephen Smalley Cc: Eric Paris Acked-by: Paul Moore Signed-off-by: David S. Miller diff --git a/include/net/sock.h b/include/net/sock.h index dfe2eb8..771ca19 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -2201,6 +2201,14 @@ static inline bool sk_fullsock(const struct sock *sk) return (1 << sk->sk_state) & ~(TCPF_TIME_WAIT | TCPF_NEW_SYN_RECV); } +/* This helper checks if a socket is a LISTEN or NEW_SYN_RECV + * SYNACK messages can be attached to either ones (depending on SYNCOOKIE) + */ +static inline bool sk_listener(const struct sock *sk) +{ + return (1 << sk->sk_state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV); +} + void sock_enable_timestamp(struct sock *sk, int flag); int sock_get_timestamp(struct sock *, struct timeval __user *); int sock_get_timestampns(struct sock *, struct timespec __user *); diff --git a/net/sched/sch_fq.c b/net/sched/sch_fq.c index 3386cce..109b232 100644 --- a/net/sched/sch_fq.c +++ b/net/sched/sch_fq.c @@ -225,6 +225,7 @@ static struct fq_flow *fq_classify(struct sk_buff *skb, struct fq_sched_data *q) return &q->internal; /* SYNACK messages are attached to a TCP_NEW_SYN_RECV request socket + * or a listener (SYNCOOKIE mode) * 1) request sockets are not full blown, * they do not contain sk_pacing_rate * 2) They are not part of a 'flow' yet @@ -232,7 +233,7 @@ static struct fq_flow *fq_classify(struct sk_buff *skb, struct fq_sched_data *q) * especially if the listener set SO_MAX_PACING_RATE * 4) We pretend they are orphaned */ - if (!sk || sk->sk_state == TCP_NEW_SYN_RECV) { + if (!sk || sk_listener(sk)) { unsigned long hash = skb_get_hash(skb) & q->orphan_mask; /* By forcing low order bit to 1, we make sure to not diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 6434016..6e50841 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4898,7 +4898,7 @@ static unsigned int selinux_ip_output(struct sk_buff *skb, if (sk) { struct sk_security_struct *sksec; - if (sk->sk_state == TCP_LISTEN) + if (sk_listener(sk)) /* if the socket is the listening state then this * packet is a SYN-ACK packet which means it needs to * be labeled based on the connection/request_sock and @@ -5005,7 +5005,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, * unfortunately, this means more work, but it is only once per * connection. */ if (skb_dst(skb) != NULL && skb_dst(skb)->xfrm != NULL && - !(sk != NULL && sk->sk_state == TCP_LISTEN)) + !(sk && sk_listener(sk))) return NF_ACCEPT; #endif @@ -5022,7 +5022,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, secmark_perm = PACKET__SEND; peer_sid = SECINITSID_KERNEL; } - } else if (sk->sk_state == TCP_LISTEN) { + } else if (sk_listener(sk)) { /* Locally generated packet but the associated socket is in the * listening state which means this is a SYN-ACK packet. In * this particular case the correct security label is assigned @@ -5033,7 +5033,11 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, * selinux_inet_conn_request(). See also selinux_ip_output() * for similar problems. */ u32 skb_sid; - struct sk_security_struct *sksec = sk->sk_security; + struct sk_security_struct *sksec; + + if (sk->sk_state == TCP_NEW_SYN_RECV) + sk = inet_reqsk(sk)->rsk_listener; + sksec = sk->sk_security; if (selinux_skb_peerlbl_sid(skb, family, &skb_sid)) return NF_DROP; /* At this point, if the returned skb peerlbl is SECSID_NULL -- cgit v0.10.2 From 8fae307c8fe9789ff1f4161dcb2db0d689069448 Mon Sep 17 00:00:00 2001 From: wangweidong Date: Thu, 8 Oct 2015 18:03:47 +0800 Subject: BNX2: fix a Null Pointer for stats_blk we have two processes to do: P1#: ifconfig eth0 down; which will call bnx2_close, then will , and set Null to stats_blk P2#: ifconfig eth0; which will call bnx2_get_stats64, it will use stats_blk. In one case: --P1#-- --P2#-- stats_blk(no null) bnx2_free_mem ->bp->stats_blk = NULL GET_64BIT_NET_STATS then it will cause 'NULL Pointer' Problem. it is as well with 'ethtool -S ethx'. Allocate the statistics block at probe time so that this problem is impossible Signed-off-by: Wang Weidong Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index 2b66ef3..6259064 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -813,6 +813,46 @@ bnx2_alloc_rx_mem(struct bnx2 *bp) } static void +bnx2_free_stats_blk(struct net_device *dev) +{ + struct bnx2 *bp = netdev_priv(dev); + + if (bp->status_blk) { + dma_free_coherent(&bp->pdev->dev, bp->status_stats_size, + bp->status_blk, + bp->status_blk_mapping); + bp->status_blk = NULL; + bp->stats_blk = NULL; + } +} + +static int +bnx2_alloc_stats_blk(struct net_device *dev) +{ + int status_blk_size; + void *status_blk; + struct bnx2 *bp = netdev_priv(dev); + + /* Combine status and statistics blocks into one allocation. */ + status_blk_size = L1_CACHE_ALIGN(sizeof(struct status_block)); + if (bp->flags & BNX2_FLAG_MSIX_CAP) + status_blk_size = L1_CACHE_ALIGN(BNX2_MAX_MSIX_HW_VEC * + BNX2_SBLK_MSIX_ALIGN_SIZE); + bp->status_stats_size = status_blk_size + + sizeof(struct statistics_block); + status_blk = dma_zalloc_coherent(&bp->pdev->dev, bp->status_stats_size, + &bp->status_blk_mapping, GFP_KERNEL); + if (status_blk == NULL) + return -ENOMEM; + + bp->status_blk = status_blk; + bp->stats_blk = status_blk + status_blk_size; + bp->stats_blk_mapping = bp->status_blk_mapping + status_blk_size; + + return 0; +} + +static void bnx2_free_mem(struct bnx2 *bp) { int i; @@ -829,37 +869,19 @@ bnx2_free_mem(struct bnx2 *bp) bp->ctx_blk[i] = NULL; } } - if (bnapi->status_blk.msi) { - dma_free_coherent(&bp->pdev->dev, bp->status_stats_size, - bnapi->status_blk.msi, - bp->status_blk_mapping); + + if (bnapi->status_blk.msi) bnapi->status_blk.msi = NULL; - bp->stats_blk = NULL; - } } static int bnx2_alloc_mem(struct bnx2 *bp) { - int i, status_blk_size, err; + int i, err; struct bnx2_napi *bnapi; - void *status_blk; - - /* Combine status and statistics blocks into one allocation. */ - status_blk_size = L1_CACHE_ALIGN(sizeof(struct status_block)); - if (bp->flags & BNX2_FLAG_MSIX_CAP) - status_blk_size = L1_CACHE_ALIGN(BNX2_MAX_MSIX_HW_VEC * - BNX2_SBLK_MSIX_ALIGN_SIZE); - bp->status_stats_size = status_blk_size + - sizeof(struct statistics_block); - - status_blk = dma_zalloc_coherent(&bp->pdev->dev, bp->status_stats_size, - &bp->status_blk_mapping, GFP_KERNEL); - if (status_blk == NULL) - goto alloc_mem_err; bnapi = &bp->bnx2_napi[0]; - bnapi->status_blk.msi = status_blk; + bnapi->status_blk.msi = bp->status_blk; bnapi->hw_tx_cons_ptr = &bnapi->status_blk.msi->status_tx_quick_consumer_index0; bnapi->hw_rx_cons_ptr = @@ -870,7 +892,7 @@ bnx2_alloc_mem(struct bnx2 *bp) bnapi = &bp->bnx2_napi[i]; - sblk = (status_blk + BNX2_SBLK_MSIX_ALIGN_SIZE * i); + sblk = (bp->status_blk + BNX2_SBLK_MSIX_ALIGN_SIZE * i); bnapi->status_blk.msix = sblk; bnapi->hw_tx_cons_ptr = &sblk->status_tx_quick_consumer_index; @@ -880,10 +902,6 @@ bnx2_alloc_mem(struct bnx2 *bp) } } - bp->stats_blk = status_blk + status_blk_size; - - bp->stats_blk_mapping = bp->status_blk_mapping + status_blk_size; - if (BNX2_CHIP(bp) == BNX2_CHIP_5709) { bp->ctx_pages = 0x2000 / BNX2_PAGE_SIZE; if (bp->ctx_pages == 0) @@ -8330,6 +8348,11 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) bp->phy_addr = 1; + /* allocate stats_blk */ + rc = bnx2_alloc_stats_blk(dev); + if (rc) + goto err_out_unmap; + /* Disable WOL support if we are running on a SERDES chip. */ if (BNX2_CHIP(bp) == BNX2_CHIP_5709) bnx2_get_5709_media(bp); @@ -8586,6 +8609,7 @@ error: pci_release_regions(pdev); pci_disable_device(pdev); err_free: + bnx2_free_stats_blk(dev); free_netdev(dev); return rc; } @@ -8603,6 +8627,7 @@ bnx2_remove_one(struct pci_dev *pdev) pci_iounmap(bp->pdev, bp->regview); + bnx2_free_stats_blk(dev); kfree(bp->temp_stats_blk); if (bp->flags & BNX2_FLAG_AER_ENABLED) { diff --git a/drivers/net/ethernet/broadcom/bnx2.h b/drivers/net/ethernet/broadcom/bnx2.h index f92f76c..380234d 100644 --- a/drivers/net/ethernet/broadcom/bnx2.h +++ b/drivers/net/ethernet/broadcom/bnx2.h @@ -6928,6 +6928,7 @@ struct bnx2 { dma_addr_t status_blk_mapping; + void *status_blk; struct statistics_block *stats_blk; struct statistics_block *temp_stats_blk; dma_addr_t stats_blk_mapping; -- cgit v0.10.2 From 6e86ac120e144154d345ebb5839d50b6f713149b Mon Sep 17 00:00:00 2001 From: Jon Ringle Date: Thu, 8 Oct 2015 11:10:19 -0400 Subject: net: encx24j600: Fix typos in Kconfig Signed-off-by: Jon Ringle Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/microchip/Kconfig b/drivers/net/ethernet/microchip/Kconfig index 992b014..36a09d9 100644 --- a/drivers/net/ethernet/microchip/Kconfig +++ b/drivers/net/ethernet/microchip/Kconfig @@ -37,9 +37,9 @@ config ENCX24J600 tristate "ENCX24J600 support" depends on SPI ---help--- - Support for the Microchip ENC424J600 ethernet chip. + Support for the Microchip ENC424J600/624J600 ethernet chip. To compile this driver as a module, choose M here. The module will be - called enc424j600. + called encx24j600. endif # NET_VENDOR_MICROCHIP -- cgit v0.10.2 From 146a32067b3fde1424d737d7fb333eb0951e6419 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 8 Oct 2015 11:35:12 -0400 Subject: net: dsa: add port_fdb_prepare Push the prepare phase for FDB operations down to the DSA drivers, with a new port_fdb_prepare function. Currently only mv88e6xxx is affected. Signed-off-by: Vivien Didelot Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/dsa/mv88e6171.c b/drivers/net/dsa/mv88e6171.c index c95cfab..ca3330a 100644 --- a/drivers/net/dsa/mv88e6171.c +++ b/drivers/net/dsa/mv88e6171.c @@ -121,6 +121,7 @@ struct dsa_switch_driver mv88e6171_switch_driver = { .port_vlan_add = mv88e6xxx_port_vlan_add, .port_vlan_del = mv88e6xxx_port_vlan_del, .vlan_getnext = mv88e6xxx_vlan_getnext, + .port_fdb_prepare = mv88e6xxx_port_fdb_prepare, .port_fdb_add = mv88e6xxx_port_fdb_add, .port_fdb_del = mv88e6xxx_port_fdb_del, .port_fdb_getnext = mv88e6xxx_port_fdb_getnext, diff --git a/drivers/net/dsa/mv88e6352.c b/drivers/net/dsa/mv88e6352.c index 3736706..078a358 100644 --- a/drivers/net/dsa/mv88e6352.c +++ b/drivers/net/dsa/mv88e6352.c @@ -348,6 +348,7 @@ struct dsa_switch_driver mv88e6352_switch_driver = { .port_vlan_add = mv88e6xxx_port_vlan_add, .port_vlan_del = mv88e6xxx_port_vlan_del, .vlan_getnext = mv88e6xxx_vlan_getnext, + .port_fdb_prepare = mv88e6xxx_port_fdb_prepare, .port_fdb_add = mv88e6xxx_port_fdb_add, .port_fdb_del = mv88e6xxx_port_fdb_del, .port_fdb_getnext = mv88e6xxx_port_fdb_getnext, diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index 8e088e3..0da8651 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -1841,6 +1841,16 @@ static int _mv88e6xxx_port_fdb_load(struct dsa_switch *ds, int port, return _mv88e6xxx_atu_load(ds, &entry); } +int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_fdb *fdb, + struct switchdev_trans *trans) +{ + /* We don't need any dynamic resource from the kernel (yet), + * so skip the prepare phase. + */ + return 0; +} + int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid) { diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h index d8ec487..3042869 100644 --- a/drivers/net/dsa/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx.h @@ -474,6 +474,9 @@ int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, u16 vid, int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid); int mv88e6xxx_vlan_getnext(struct dsa_switch *ds, u16 *vid, unsigned long *ports, unsigned long *untagged); +int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_fdb *fdb, + struct switchdev_trans *trans); int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid); int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, diff --git a/include/net/dsa.h b/include/net/dsa.h index b34d812..4f66f84 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -197,6 +197,9 @@ static inline u8 dsa_upstream_port(struct dsa_switch *ds) return ds->pd->rtable[dst->cpu_switch]; } +struct switchdev_trans; +struct switchdev_obj_port_fdb; + struct dsa_switch_driver { struct list_head list; @@ -316,6 +319,9 @@ struct dsa_switch_driver { /* * Forwarding database */ + int (*port_fdb_prepare)(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_fdb *fdb, + struct switchdev_trans *trans); int (*port_fdb_add)(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid); int (*port_fdb_del)(struct dsa_switch *ds, int port, diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 4f607bc..48e8c15 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -346,10 +346,13 @@ static int dsa_slave_port_fdb_add(struct net_device *dev, { struct dsa_slave_priv *p = netdev_priv(dev); struct dsa_switch *ds = p->parent; - int ret = -EOPNOTSUPP; + int ret; + + if (!ds->drv->port_fdb_prepare || !ds->drv->port_fdb_add) + return -EOPNOTSUPP; if (switchdev_trans_ph_prepare(trans)) - ret = ds->drv->port_fdb_add ? 0 : -EOPNOTSUPP; + ret = ds->drv->port_fdb_prepare(ds, p->port, fdb, trans); else ret = ds->drv->port_fdb_add(ds, p->port, fdb->addr, fdb->vid); -- cgit v0.10.2 From 1f36faf26943f5f5fc1d1a7be6ce252d2ff25e1a Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 8 Oct 2015 11:35:13 -0400 Subject: net: dsa: push prepare phase in port_fdb_add Now that the prepare phase is pushed down to the DSA drivers, propagate it to the port_fdb_add function. Signed-off-by: Vivien Didelot Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index 0da8651..644fffc 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "mv88e6xxx.h" /* MDIO bus access can be nested in the case of PHYs connected to the @@ -1852,16 +1853,17 @@ int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port, } int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const struct switchdev_obj_port_fdb *fdb, + struct switchdev_trans *trans) { - int state = is_multicast_ether_addr(addr) ? + int state = is_multicast_ether_addr(fdb->addr) ? GLOBAL_ATU_DATA_STATE_MC_STATIC : GLOBAL_ATU_DATA_STATE_UC_STATIC; struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int ret; mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_port_fdb_load(ds, port, addr, vid, state); + ret = _mv88e6xxx_port_fdb_load(ds, port, fdb->addr, fdb->vid, state); mutex_unlock(&ps->smi_mutex); return ret; diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h index 3042869..86a94dc 100644 --- a/drivers/net/dsa/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx.h @@ -478,7 +478,8 @@ int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port, const struct switchdev_obj_port_fdb *fdb, struct switchdev_trans *trans); int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid); + const struct switchdev_obj_port_fdb *fdb, + struct switchdev_trans *trans); int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid); int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port, diff --git a/include/net/dsa.h b/include/net/dsa.h index 4f66f84..ce8f9b7 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -323,7 +323,8 @@ struct dsa_switch_driver { const struct switchdev_obj_port_fdb *fdb, struct switchdev_trans *trans); int (*port_fdb_add)(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid); + const struct switchdev_obj_port_fdb *fdb, + struct switchdev_trans *trans); int (*port_fdb_del)(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid); int (*port_fdb_getnext)(struct dsa_switch *ds, int port, diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 48e8c15..6f7f27e 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -354,7 +354,7 @@ static int dsa_slave_port_fdb_add(struct net_device *dev, if (switchdev_trans_ph_prepare(trans)) ret = ds->drv->port_fdb_prepare(ds, p->port, fdb, trans); else - ret = ds->drv->port_fdb_add(ds, p->port, fdb->addr, fdb->vid); + ret = ds->drv->port_fdb_add(ds, p->port, fdb, trans); return ret; } -- cgit v0.10.2 From 8057b3e7a1cfb4da61717ba609e1ea642bb82f9b Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 8 Oct 2015 11:35:14 -0400 Subject: net: dsa: use switchdev obj in port_fdb_del For consistency with the FDB add operation, propagate the switchdev_obj_port_fdb structure in the DSA drivers. Signed-off-by: Vivien Didelot Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index 644fffc..87b405e 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -1870,13 +1870,13 @@ int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, } int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const struct switchdev_obj_port_fdb *fdb) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int ret; mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_port_fdb_load(ds, port, addr, vid, + ret = _mv88e6xxx_port_fdb_load(ds, port, fdb->addr, fdb->vid, GLOBAL_ATU_DATA_STATE_UNUSED); mutex_unlock(&ps->smi_mutex); diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h index 86a94dc..8325c11 100644 --- a/drivers/net/dsa/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx.h @@ -481,7 +481,7 @@ int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, const struct switchdev_obj_port_fdb *fdb, struct switchdev_trans *trans); int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid); + const struct switchdev_obj_port_fdb *fdb); int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port, unsigned char *addr, u16 *vid, bool *is_static); int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg); diff --git a/include/net/dsa.h b/include/net/dsa.h index ce8f9b7..e005886 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -326,7 +326,7 @@ struct dsa_switch_driver { const struct switchdev_obj_port_fdb *fdb, struct switchdev_trans *trans); int (*port_fdb_del)(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid); + const struct switchdev_obj_port_fdb *fdb); int (*port_fdb_getnext)(struct dsa_switch *ds, int port, unsigned char *addr, u16 *vid, bool *is_static); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 6f7f27e..bb2bd3b 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -367,7 +367,7 @@ static int dsa_slave_port_fdb_del(struct net_device *dev, int ret = -EOPNOTSUPP; if (ds->drv->port_fdb_del) - ret = ds->drv->port_fdb_del(ds, p->port, fdb->addr, fdb->vid); + ret = ds->drv->port_fdb_del(ds, p->port, fdb); return ret; } -- cgit v0.10.2 From 6bcfd7f8c28887a4298bc4386b02cb90c9fa0c13 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 8 Oct 2015 11:16:48 -0700 Subject: tcp: fix RFS vs lockless listeners Before recent TCP listener patches, we were updating listener sk->sk_rxhash before the cloning of master socket. children sk_rxhash was therefore correct after the normal 3WHS. But with lockless listener, we no longer dirty/change listener sk_rxhash as it would be racy. We need to correctly update the child sk_rxhash, otherwise first data packet wont hit correct cpu if RFS is used. Fixes: 079096f103fa ("tcp/dccp: install syn_recv requests into ehash table") Signed-off-by: Eric Dumazet Reported-by: Willem de Bruijn Cc: Tom Herbert Acked-by: Tom Herbert Signed-off-by: David S. Miller diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 8113c30..2dbb113 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -225,6 +225,7 @@ struct sock *tcp_get_cookie_sock(struct sock *sk, struct sk_buff *skb, child = icsk->icsk_af_ops->syn_recv_sock(sk, skb, req, dst); if (child) { atomic_set(&req->rsk_refcnt, 1); + sock_rps_save_rxhash(child, skb); inet_csk_reqsk_queue_add(sk, req, child); } else { reqsk_free(req); diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 9adf1e2..1079e6a 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -768,6 +768,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, if (!child) goto listen_overflow; + sock_rps_save_rxhash(child, skb); tcp_synack_rtt_meas(child, req); inet_csk_reqsk_queue_drop(sk, req); inet_csk_reqsk_queue_add(sk, req, child); -- cgit v0.10.2 From 3741873b4f73b572b8f8835e6bd114e08316a160 Mon Sep 17 00:00:00 2001 From: Roopa Prabhu Date: Thu, 8 Oct 2015 10:38:52 -0700 Subject: bridge: allow adding of fdb entries pointing to the bridge device This patch enables adding of fdb entries pointing to the bridge device. This can be used to propagate mac address of vlan interfaces configured on top of the vlan filtering bridge. Before: $bridge fdb add 44:38:39:00:27:9f dev bridge RTNETLINK answers: Invalid argument After: $bridge fdb add 44:38:39:00:27:9f dev bridge Signed-off-by: Roopa Prabhu Reviewed-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 7f7d551..f43ce05 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -608,13 +608,14 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, } } -static int fdb_to_nud(const struct net_bridge_fdb_entry *fdb) +static int fdb_to_nud(const struct net_bridge *br, + const struct net_bridge_fdb_entry *fdb) { if (fdb->is_local) return NUD_PERMANENT; else if (fdb->is_static) return NUD_NOARP; - else if (has_expired(fdb->dst->br, fdb)) + else if (has_expired(br, fdb)) return NUD_STALE; else return NUD_REACHABLE; @@ -640,7 +641,7 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br, ndm->ndm_flags = fdb->added_by_external_learn ? NTF_EXT_LEARNED : 0; ndm->ndm_type = 0; ndm->ndm_ifindex = fdb->dst ? fdb->dst->dev->ifindex : br->dev->ifindex; - ndm->ndm_state = fdb_to_nud(fdb); + ndm->ndm_state = fdb_to_nud(br, fdb); if (nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->addr)) goto nla_put_failure; @@ -785,7 +786,7 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr, } } - if (fdb_to_nud(fdb) != state) { + if (fdb_to_nud(br, fdb) != state) { if (state & NUD_PERMANENT) { fdb->is_local = 1; if (!fdb->is_static) { @@ -846,8 +847,9 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], const unsigned char *addr, u16 vid, u16 nlh_flags) { struct net_bridge_vlan_group *vg; - struct net_bridge_port *p; + struct net_bridge_port *p = NULL; struct net_bridge_vlan *v; + struct net_bridge *br = NULL; int err = 0; if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE))) { @@ -860,26 +862,36 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], return -EINVAL; } - p = br_port_get_rtnl(dev); - if (p == NULL) { - pr_info("bridge: RTM_NEWNEIGH %s not a bridge port\n", - dev->name); - return -EINVAL; + if (dev->priv_flags & IFF_EBRIDGE) { + br = netdev_priv(dev); + vg = br_vlan_group(br); + } else { + p = br_port_get_rtnl(dev); + if (!p) { + pr_info("bridge: RTM_NEWNEIGH %s not a bridge port\n", + dev->name); + return -EINVAL; + } + vg = nbp_vlan_group(p); } - vg = nbp_vlan_group(p); if (vid) { v = br_vlan_find(vg, vid); - if (!v) { - pr_info("bridge: RTM_NEWNEIGH with unconfigured " - "vlan %d on port %s\n", vid, dev->name); + if (!v || !br_vlan_should_use(v)) { + pr_info("bridge: RTM_NEWNEIGH with unconfigured vlan %d on %s\n", vid, dev->name); return -EINVAL; } /* VID was specified, so use it. */ - err = __br_fdb_add(ndm, p, addr, nlh_flags, vid); + if (dev->priv_flags & IFF_EBRIDGE) + err = br_fdb_insert(br, NULL, addr, vid); + else + err = __br_fdb_add(ndm, p, addr, nlh_flags, vid); } else { - err = __br_fdb_add(ndm, p, addr, nlh_flags, 0); + if (dev->priv_flags & IFF_EBRIDGE) + err = br_fdb_insert(br, NULL, addr, 0); + else + err = __br_fdb_add(ndm, p, addr, nlh_flags, 0); if (err || !vg || !vg->num_vlans) goto out; @@ -888,7 +900,13 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], * vlan on this port. */ list_for_each_entry(v, &vg->vlan_list, vlist) { - err = __br_fdb_add(ndm, p, addr, nlh_flags, v->vid); + if (!br_vlan_should_use(v)) + continue; + if (dev->priv_flags & IFF_EBRIDGE) + err = br_fdb_insert(br, NULL, addr, v->vid); + else + err = __br_fdb_add(ndm, p, addr, nlh_flags, + v->vid); if (err) goto out; } @@ -898,6 +916,32 @@ out: return err; } +static int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, + u16 vid) +{ + struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)]; + struct net_bridge_fdb_entry *fdb; + + fdb = fdb_find(head, addr, vid); + if (!fdb) + return -ENOENT; + + fdb_delete(br, fdb); + return 0; +} + +static int __br_fdb_delete_by_addr(struct net_bridge *br, + const unsigned char *addr, u16 vid) +{ + int err; + + spin_lock_bh(&br->hash_lock); + err = fdb_delete_by_addr(br, addr, vid); + spin_unlock_bh(&br->hash_lock); + + return err; +} + static int fdb_delete_by_addr_and_port(struct net_bridge_port *p, const u8 *addr, u16 vlan) { @@ -931,35 +975,53 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], const unsigned char *addr, u16 vid) { struct net_bridge_vlan_group *vg; - struct net_bridge_port *p; + struct net_bridge_port *p = NULL; struct net_bridge_vlan *v; + struct net_bridge *br = NULL; int err; - p = br_port_get_rtnl(dev); - if (p == NULL) { - pr_info("bridge: RTM_DELNEIGH %s not a bridge port\n", - dev->name); - return -EINVAL; + if (dev->priv_flags & IFF_EBRIDGE) { + br = netdev_priv(dev); + vg = br_vlan_group(br); + } else { + p = br_port_get_rtnl(dev); + if (!p) { + pr_info("bridge: RTM_DELNEIGH %s not a bridge port\n", + dev->name); + return -EINVAL; + } + vg = nbp_vlan_group(p); } - vg = nbp_vlan_group(p); if (vid) { v = br_vlan_find(vg, vid); if (!v) { - pr_info("bridge: RTM_DELNEIGH with unconfigured " - "vlan %d on port %s\n", vid, dev->name); + pr_info("bridge: RTM_DELNEIGH with unconfigured vlan %d on %s\n", vid, dev->name); return -EINVAL; } - err = __br_fdb_delete(p, addr, vid); + if (dev->priv_flags & IFF_EBRIDGE) + err = __br_fdb_delete_by_addr(br, addr, vid); + else + err = __br_fdb_delete(p, addr, vid); } else { err = -ENOENT; - err &= __br_fdb_delete(p, addr, 0); + if (dev->priv_flags & IFF_EBRIDGE) + err = __br_fdb_delete_by_addr(br, addr, 0); + else + err &= __br_fdb_delete(p, addr, 0); + if (!vg || !vg->num_vlans) goto out; - list_for_each_entry(v, &vg->vlan_list, vlist) - err &= __br_fdb_delete(p, addr, v->vid); + list_for_each_entry(v, &vg->vlan_list, vlist) { + if (!br_vlan_should_use(v)) + continue; + if (dev->priv_flags & IFF_EBRIDGE) + err = __br_fdb_delete_by_addr(br, addr, v->vid); + else + err &= __br_fdb_delete(p, addr, v->vid); + } } out: return err; diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index eae07ee..7a95e31 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -564,6 +564,7 @@ int br_vlan_delete(struct net_bridge *br, u16 vid) return -ENOENT; br_fdb_find_delete_local(br, NULL, br->dev->dev_addr, vid); + br_fdb_delete_by_port(br, NULL, vid, 0); return __vlan_del(v); } -- cgit v0.10.2 From 7533ce3055bbe9577276a847125b156c44a5bbce Mon Sep 17 00:00:00 2001 From: Richard Sailer Date: Fri, 9 Oct 2015 02:41:37 +0200 Subject: tcp: change type of alive from int to bool The alive parameter of tcp_orphan_retries, indicates whether the connection is assumed alive or not. In the function and all places calling it is used as a boolean value. Therefore this changes the type of alive to bool in the function definition and all calling locations. Since tcp_orphan_tries is a tcp_timer.c local function no change in any other file or header is necessary. Signed-off-by: Richard Sailer Signed-off-by: David S. Miller diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 7149ebc..c9c716a 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -83,7 +83,7 @@ static int tcp_out_of_resources(struct sock *sk, bool do_reset) } /* Calculate maximal number or retries on an orphaned socket. */ -static int tcp_orphan_retries(struct sock *sk, int alive) +static int tcp_orphan_retries(struct sock *sk, bool alive) { int retries = sysctl_tcp_orphan_retries; /* May be zero. */ @@ -184,7 +184,7 @@ static int tcp_write_timeout(struct sock *sk) retry_until = sysctl_tcp_retries2; if (sock_flag(sk, SOCK_DEAD)) { - const int alive = icsk->icsk_rto < TCP_RTO_MAX; + const bool alive = icsk->icsk_rto < TCP_RTO_MAX; retry_until = tcp_orphan_retries(sk, alive); do_reset = alive || @@ -298,7 +298,7 @@ static void tcp_probe_timer(struct sock *sk) max_probes = sysctl_tcp_retries2; if (sock_flag(sk, SOCK_DEAD)) { - const int alive = inet_csk_rto_backoff(icsk, TCP_RTO_MAX) < TCP_RTO_MAX; + const bool alive = inet_csk_rto_backoff(icsk, TCP_RTO_MAX) < TCP_RTO_MAX; max_probes = tcp_orphan_retries(sk, alive); if (!alive && icsk->icsk_backoff >= max_probes) -- cgit v0.10.2 From f55ac58ae64cbb0315382e738681fe31837dcac0 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Thu, 8 Oct 2015 19:23:17 -0700 Subject: switchdev: add bridge ageing_time attribute Setting the stage to push bridge-level attributes down to port driver so hardware can be programmed accordingly. Bridge-level attribute example is ageing_time. This is a per-bridge attribute, not a per-bridge-port attr. Signed-off-by: Scott Feldman Acked-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/include/net/switchdev.h b/include/net/switchdev.h index 89266a3..61f129b 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -43,6 +43,7 @@ enum switchdev_attr_id { SWITCHDEV_ATTR_ID_PORT_PARENT_ID, SWITCHDEV_ATTR_ID_PORT_STP_STATE, SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS, + SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME, }; struct switchdev_attr { @@ -52,6 +53,7 @@ struct switchdev_attr { struct netdev_phys_item_id ppid; /* PORT_PARENT_ID */ u8 stp_state; /* PORT_STP_STATE */ unsigned long brport_flags; /* PORT_BRIDGE_FLAGS */ + u32 ageing_time; /* BRIDGE_AGEING_TIME */ } u; }; -- cgit v0.10.2 From 464314ea6c119ebc22ee78453e63814453c31611 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Thu, 8 Oct 2015 19:23:18 -0700 Subject: switchdev: skip over ports returning -EOPNOTSUPP when recursing ports This allows us to recurse over all the ports, skipping over unsupporting ports. Without the change, the recursion would stop at first unsupported port. Signed-off-by: Scott Feldman Acked-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/include/net/switchdev.h b/include/net/switchdev.h index 61f129b..1ce7083 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -16,6 +16,7 @@ #include #define SWITCHDEV_F_NO_RECURSE BIT(0) +#define SWITCHDEV_F_SKIP_EOPNOTSUPP BIT(1) struct switchdev_trans_item { struct list_head list; diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index 6e4a4f9..7a9ab90 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -147,7 +147,7 @@ static int __switchdev_port_attr_set(struct net_device *dev, return ops->switchdev_port_attr_set(dev, attr, trans); if (attr->flags & SWITCHDEV_F_NO_RECURSE) - return err; + goto done; /* Switch device port(s) may be stacked under * bond/team/vlan dev, so recurse down to set attr on @@ -156,10 +156,17 @@ static int __switchdev_port_attr_set(struct net_device *dev, netdev_for_each_lower_dev(dev, lower_dev, iter) { err = __switchdev_port_attr_set(lower_dev, attr, trans); + if (err == -EOPNOTSUPP && + attr->flags & SWITCHDEV_F_SKIP_EOPNOTSUPP) + continue; if (err) break; } +done: + if (err == -EOPNOTSUPP && attr->flags & SWITCHDEV_F_SKIP_EOPNOTSUPP) + err = 0; + return err; } -- cgit v0.10.2 From c62987bbd8a1a1664f99e89e3959339350a6131e Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Thu, 8 Oct 2015 19:23:19 -0700 Subject: bridge: push bridge setting ageing_time down to switchdev Use SWITCHDEV_F_SKIP_EOPNOTSUPP to skip over ports in bridge that don't support setting ageing_time (or setting bridge attrs in general). If push fails, don't update ageing_time in bridge and return err to user. If push succeeds, update ageing_time in bridge and run gc_timer now to recalabrate when to run gc_timer next, based on new ageing_time. Signed-off-by: Scott Feldman Signed-off-by: Jiri Pirko Acked-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c index 8d423bc..263b4de 100644 --- a/net/bridge/br_ioctl.c +++ b/net/bridge/br_ioctl.c @@ -200,8 +200,7 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN)) return -EPERM; - br->ageing_time = clock_t_to_jiffies(args[1]); - return 0; + return br_set_ageing_time(br, args[1]); case BRCTL_GET_PORT_INFO: { diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index d78b442..544ab96 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -870,9 +870,9 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[], } if (data[IFLA_BR_AGEING_TIME]) { - u32 ageing_time = nla_get_u32(data[IFLA_BR_AGEING_TIME]); - - br->ageing_time = clock_t_to_jiffies(ageing_time); + err = br_set_ageing_time(br, nla_get_u32(data[IFLA_BR_AGEING_TIME])); + if (err) + return err; } if (data[IFLA_BR_STP_STATE]) { diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 09d3ecb..ba0c67b 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -882,6 +882,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 br_set_ageing_time(struct net_bridge *br, u32 ageing_time); /* br_stp_if.c */ diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index 3a982c0..db6d243de 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -566,6 +566,29 @@ int br_set_max_age(struct net_bridge *br, unsigned long val) } +int br_set_ageing_time(struct net_bridge *br, u32 ageing_time) +{ + struct switchdev_attr attr = { + .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; + + if (t < BR_MIN_AGEING_TIME || t > BR_MAX_AGEING_TIME) + return -ERANGE; + + err = switchdev_port_attr_set(br->dev, &attr); + if (err) + return err; + + br->ageing_time = t; + mod_timer(&br->gc_timer, jiffies); + + return 0; +} + void __br_set_forward_delay(struct net_bridge *br, unsigned long t) { br->bridge_forward_delay = t; diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c index 4c97fc5..04ef192 100644 --- a/net/bridge/br_sysfs_br.c +++ b/net/bridge/br_sysfs_br.c @@ -102,8 +102,7 @@ static ssize_t ageing_time_show(struct device *d, static int set_ageing_time(struct net_bridge *br, unsigned long val) { - br->ageing_time = clock_t_to_jiffies(val); - return 0; + return br_set_ageing_time(br, val); } static ssize_t ageing_time_store(struct device *d, -- cgit v0.10.2 From d0cf57f9dddb50ea404bf747a3c6b22b29f82b9a Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Thu, 8 Oct 2015 19:23:20 -0700 Subject: rocker: handle setting bridge ageing_time The FDB cleanup timer will get rescheduled to re-evaluate FDB entries based on new ageing_time. Signed-off-by: Scott Feldman Acked-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c index cf91ffc..eafa907 100644 --- a/drivers/net/ethernet/rocker/rocker.c +++ b/drivers/net/ethernet/rocker/rocker.c @@ -4361,6 +4361,18 @@ static int rocker_port_brport_flags_set(struct rocker_port *rocker_port, return err; } +static int rocker_port_bridge_ageing_time(struct rocker_port *rocker_port, + struct switchdev_trans *trans, + u32 ageing_time) +{ + if (!switchdev_trans_ph_prepare(trans)) { + rocker_port->ageing_time = clock_t_to_jiffies(ageing_time); + mod_timer(&rocker_port->rocker->fdb_cleanup_timer, jiffies); + } + + return 0; +} + static int rocker_port_attr_set(struct net_device *dev, struct switchdev_attr *attr, struct switchdev_trans *trans) @@ -4378,6 +4390,10 @@ static int rocker_port_attr_set(struct net_device *dev, err = rocker_port_brport_flags_set(rocker_port, trans, attr->u.brport_flags); break; + case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: + err = rocker_port_bridge_ageing_time(rocker_port, trans, + attr->u.ageing_time); + break; default: err = -EOPNOTSUPP; break; -- cgit v0.10.2 From c577e59ed7f55be398a2a730447a7f37d72eaa57 Mon Sep 17 00:00:00 2001 From: Daniel Pieczko Date: Fri, 9 Oct 2015 10:40:35 +0100 Subject: sfc: fully reset if MC_REBOOT event received without warm_boot_count increment On EF10, MC_CMD_VPORT_RECONFIGURE can cause a CODE_MC_REBOOT event to be sent to a function without incrementing the (adapter-wide) warm_boot_count. In this case, the reboot is not detected by the loop on efx_mcdi_poll_reboot(), so prepare for recovery from an MC reboot anyway. When this codepath is run, the MC has always just rebooted, so this recovery is valid. The loop on efx_mcdi_poll_reboot() is still required for other MC reboot cases, so that actions in response to an MC reboot are performed, such as clearing locally calculated statistics. Siena NICs are unaffected by this change as the above scenario does not apply. Signed-off-by: Shradha Shah Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index ff649eb..78b7b7b 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -1604,6 +1604,22 @@ efx_ef10_mcdi_read_response(struct efx_nic *efx, efx_dword_t *outbuf, memcpy(outbuf, pdu + offset, outlen); } +static void efx_ef10_mcdi_reboot_detected(struct efx_nic *efx) +{ + struct efx_ef10_nic_data *nic_data = efx->nic_data; + + /* All our allocations have been reset */ + efx_ef10_reset_mc_allocations(efx); + + /* The datapath firmware might have been changed */ + nic_data->must_check_datapath_caps = true; + + /* MAC statistics have been cleared on the NIC; clear the local + * statistic that we update with efx_update_diff_stat(). + */ + nic_data->stats[EF10_STAT_port_rx_bad_bytes] = 0; +} + static int efx_ef10_mcdi_poll_reboot(struct efx_nic *efx) { struct efx_ef10_nic_data *nic_data = efx->nic_data; @@ -1623,17 +1639,7 @@ static int efx_ef10_mcdi_poll_reboot(struct efx_nic *efx) return 0; nic_data->warm_boot_count = rc; - - /* All our allocations have been reset */ - efx_ef10_reset_mc_allocations(efx); - - /* The datapath firmware might have been changed */ - nic_data->must_check_datapath_caps = true; - - /* MAC statistics have been cleared on the NIC; clear the local - * statistic that we update with efx_update_diff_stat(). - */ - nic_data->stats[EF10_STAT_port_rx_bad_bytes] = 0; + efx_ef10_mcdi_reboot_detected(efx); return -EIO; } @@ -4670,6 +4676,7 @@ const struct efx_nic_type efx_hunt_a0_vf_nic_type = { .mcdi_poll_response = efx_ef10_mcdi_poll_response, .mcdi_read_response = efx_ef10_mcdi_read_response, .mcdi_poll_reboot = efx_ef10_mcdi_poll_reboot, + .mcdi_reboot_detected = efx_ef10_mcdi_reboot_detected, .irq_enable_master = efx_port_dummy_op_void, .irq_test_generate = efx_ef10_irq_test_generate, .irq_disable_non_ev = efx_port_dummy_op_void, @@ -4774,6 +4781,7 @@ const struct efx_nic_type efx_hunt_a0_nic_type = { .mcdi_poll_response = efx_ef10_mcdi_poll_response, .mcdi_read_response = efx_ef10_mcdi_read_response, .mcdi_poll_reboot = efx_ef10_mcdi_poll_reboot, + .mcdi_reboot_detected = efx_ef10_mcdi_reboot_detected, .irq_enable_master = efx_port_dummy_op_void, .irq_test_generate = efx_ef10_irq_test_generate, .irq_disable_non_ev = efx_port_dummy_op_void, diff --git a/drivers/net/ethernet/sfc/mcdi.c b/drivers/net/ethernet/sfc/mcdi.c index 98d172b..d3f307e 100644 --- a/drivers/net/ethernet/sfc/mcdi.c +++ b/drivers/net/ethernet/sfc/mcdi.c @@ -1028,10 +1028,21 @@ static void efx_mcdi_ev_death(struct efx_nic *efx, int rc) /* Consume the status word since efx_mcdi_rpc_finish() won't */ for (count = 0; count < MCDI_STATUS_DELAY_COUNT; ++count) { - if (efx_mcdi_poll_reboot(efx)) + rc = efx_mcdi_poll_reboot(efx); + if (rc) break; udelay(MCDI_STATUS_DELAY_US); } + + /* On EF10, a CODE_MC_REBOOT event can be received without the + * reboot detection in efx_mcdi_poll_reboot() being triggered. + * If zero was returned from the final call to + * efx_mcdi_poll_reboot(), the MC reboot wasn't noticed but the + * MC has definitely rebooted so prepare for the reset. + */ + if (!rc && efx->type->mcdi_reboot_detected) + efx->type->mcdi_reboot_detected(efx); + mcdi->new_epoch = true; /* Nobody was waiting for an MCDI request, so trigger a reset */ diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index c530e1c..ad56231 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -1277,6 +1277,7 @@ struct efx_nic_type { void (*mcdi_read_response)(struct efx_nic *efx, efx_dword_t *pdu, size_t pdu_offset, size_t pdu_len); int (*mcdi_poll_reboot)(struct efx_nic *efx); + void (*mcdi_reboot_detected)(struct efx_nic *efx); void (*irq_enable_master)(struct efx_nic *efx); void (*irq_test_generate)(struct efx_nic *efx); void (*irq_disable_non_ev)(struct efx_nic *efx); -- cgit v0.10.2 From 0fa28877b26641cca56b607ccec1fcbda7ae09c6 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 9 Oct 2015 14:53:54 +0200 Subject: net: HNS: fix MDIO dependencies The newly introduced HNS_MDIO Kconfig symbol selects 'MDIO', but that is the wrong symbol as the code used by this driver is provided by PHYLIB rather than the MDIO driver. Also, there is no need to make this driver user selectable, because it is already selected by all drivers that need it. This changes the Kconfig file to select the correct library, and to make the option silent. Signed-off-by: Arnd Bergmann Fixes: 5b904d39406 ("net: add Hisilicon Network Subsystem MDIO support") Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig index 165b5a8..8d12b58 100644 --- a/drivers/net/ethernet/hisilicon/Kconfig +++ b/drivers/net/ethernet/hisilicon/Kconfig @@ -24,7 +24,6 @@ config HIX5HD2_GMAC config HIP04_ETH tristate "HISILICON P04 Ethernet support" - select PHYLIB select MARVELL_PHY select MFD_SYSCON select HNS_MDIO @@ -33,8 +32,8 @@ config HIP04_ETH want to use the internal ethernet then you should answer Y to this. config HNS_MDIO - tristate "Hisilicon HNS MDIO device Support" - select MDIO + tristate + select PHYLIB ---help--- This selects the HNS MDIO support. It is needed by HNS_DSAF to access the PHY -- cgit v0.10.2 From 1be7f75d1668d6296b80bf35dcf6762393530afc Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Wed, 7 Oct 2015 22:23:21 -0700 Subject: bpf: enable non-root eBPF programs In order to let unprivileged users load and execute eBPF programs teach verifier to prevent pointer leaks. Verifier will prevent - any arithmetic on pointers (except R10+Imm which is used to compute stack addresses) - comparison of pointers (except if (map_value_ptr == 0) ... ) - passing pointers to helper functions - indirectly passing pointers in stack to helper functions - returning pointer from bpf program - storing pointers into ctx or maps Spill/fill of pointers into stack is allowed, but mangling of pointers stored in the stack or reading them byte by byte is not. Within bpf programs the pointers do exist, since programs need to be able to access maps, pass skb pointer to LD_ABS insns, etc but programs cannot pass such pointer values to the outside or obfuscate them. Only allow BPF_PROG_TYPE_SOCKET_FILTER unprivileged programs, so that socket filters (tcpdump), af_packet (quic acceleration) and future kcm can use it. tracing and tc cls/act program types still require root permissions, since tracing actually needs to be able to see all kernel pointers and tc is for root only. For example, the following unprivileged socket filter program is allowed: int bpf_prog1(struct __sk_buff *skb) { u32 index = load_byte(skb, ETH_HLEN + offsetof(struct iphdr, protocol)); u64 *value = bpf_map_lookup_elem(&my_map, &index); if (value) *value += skb->len; return 0; } but the following program is not: int bpf_prog1(struct __sk_buff *skb) { u32 index = load_byte(skb, ETH_HLEN + offsetof(struct iphdr, protocol)); u64 *value = bpf_map_lookup_elem(&my_map, &index); if (value) *value += (u64) skb; return 0; } since it would leak the kernel address into the map. Unprivileged socket filter bpf programs have access to the following helper functions: - map lookup/update/delete (but they cannot store kernel pointers into them) - get_random (it's already exposed to unprivileged user space) - get_smp_processor_id - tail_call into another socket filter program - ktime_get_ns The feature is controlled by sysctl kernel.unprivileged_bpf_disabled. This toggle defaults to off (0), but can be set true (1). Once true, bpf programs and maps cannot be accessed from unprivileged process, and the toggle cannot be set back to false. Signed-off-by: Alexei Starovoitov Reviewed-by: Kees Cook Signed-off-by: David S. Miller diff --git a/include/linux/bpf.h b/include/linux/bpf.h index b4fdee6..02fa3db 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -167,6 +167,8 @@ void bpf_prog_put_rcu(struct bpf_prog *prog); struct bpf_map *bpf_map_get(struct fd f); void bpf_map_put(struct bpf_map *map); +extern int sysctl_unprivileged_bpf_disabled; + /* verify correctness of eBPF program */ int bpf_check(struct bpf_prog **fp, union bpf_attr *attr); #else diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index c868caf..83697bc 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -18,6 +18,8 @@ #include #include +int sysctl_unprivileged_bpf_disabled __read_mostly; + static LIST_HEAD(bpf_map_types); static struct bpf_map *find_and_alloc_map(union bpf_attr *attr) @@ -544,6 +546,9 @@ static int bpf_prog_load(union bpf_attr *attr) attr->kern_version != LINUX_VERSION_CODE) return -EINVAL; + if (type != BPF_PROG_TYPE_SOCKET_FILTER && !capable(CAP_SYS_ADMIN)) + return -EPERM; + /* plain bpf_prog allocation */ prog = bpf_prog_alloc(bpf_prog_size(attr->insn_cnt), GFP_USER); if (!prog) @@ -599,11 +604,7 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz union bpf_attr attr = {}; int err; - /* the syscall is limited to root temporarily. This restriction will be - * lifted when security audit is clean. Note that eBPF+tracing must have - * this restriction, since it may pass kernel data to user space - */ - if (!capable(CAP_SYS_ADMIN)) + if (!capable(CAP_SYS_ADMIN) && sysctl_unprivileged_bpf_disabled) return -EPERM; if (!access_ok(VERIFY_READ, uattr, 1)) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index f8da034..1d6b97b 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -199,6 +199,7 @@ struct verifier_env { struct verifier_state_list **explored_states; /* search pruning optimization */ struct bpf_map *used_maps[MAX_USED_MAPS]; /* array of map's used by eBPF program */ u32 used_map_cnt; /* number of used maps */ + bool allow_ptr_leaks; }; /* verbose verifier prints what it's seeing @@ -538,6 +539,21 @@ static int bpf_size_to_bytes(int bpf_size) return -EINVAL; } +static bool is_spillable_regtype(enum bpf_reg_type type) +{ + switch (type) { + case PTR_TO_MAP_VALUE: + case PTR_TO_MAP_VALUE_OR_NULL: + case PTR_TO_STACK: + case PTR_TO_CTX: + case FRAME_PTR: + case CONST_PTR_TO_MAP: + return true; + default: + return false; + } +} + /* check_stack_read/write functions track spill/fill of registers, * stack boundary and alignment are checked in check_mem_access() */ @@ -550,9 +566,7 @@ static int check_stack_write(struct verifier_state *state, int off, int size, */ if (value_regno >= 0 && - (state->regs[value_regno].type == PTR_TO_MAP_VALUE || - state->regs[value_regno].type == PTR_TO_STACK || - state->regs[value_regno].type == PTR_TO_CTX)) { + is_spillable_regtype(state->regs[value_regno].type)) { /* register containing pointer is being spilled into stack */ if (size != BPF_REG_SIZE) { @@ -643,6 +657,20 @@ static int check_ctx_access(struct verifier_env *env, int off, int size, return -EACCES; } +static bool is_pointer_value(struct verifier_env *env, int regno) +{ + if (env->allow_ptr_leaks) + return false; + + switch (env->cur_state.regs[regno].type) { + case UNKNOWN_VALUE: + case CONST_IMM: + return false; + default: + return true; + } +} + /* check whether memory at (regno + off) is accessible for t = (read | write) * if t==write, value_regno is a register which value is stored into memory * if t==read, value_regno is a register which will receive the value from memory @@ -669,11 +697,21 @@ static int check_mem_access(struct verifier_env *env, u32 regno, int off, } if (state->regs[regno].type == PTR_TO_MAP_VALUE) { + if (t == BPF_WRITE && value_regno >= 0 && + is_pointer_value(env, value_regno)) { + verbose("R%d leaks addr into map\n", value_regno); + return -EACCES; + } err = check_map_access(env, regno, off, size); if (!err && t == BPF_READ && value_regno >= 0) mark_reg_unknown_value(state->regs, value_regno); } else if (state->regs[regno].type == PTR_TO_CTX) { + if (t == BPF_WRITE && value_regno >= 0 && + is_pointer_value(env, value_regno)) { + verbose("R%d leaks addr into ctx\n", value_regno); + return -EACCES; + } err = check_ctx_access(env, off, size, t); if (!err && t == BPF_READ && value_regno >= 0) mark_reg_unknown_value(state->regs, value_regno); @@ -684,10 +722,17 @@ static int check_mem_access(struct verifier_env *env, u32 regno, int off, verbose("invalid stack off=%d size=%d\n", off, size); return -EACCES; } - if (t == BPF_WRITE) + if (t == BPF_WRITE) { + if (!env->allow_ptr_leaks && + state->stack_slot_type[MAX_BPF_STACK + off] == STACK_SPILL && + size != BPF_REG_SIZE) { + verbose("attempt to corrupt spilled pointer on stack\n"); + return -EACCES; + } err = check_stack_write(state, off, size, value_regno); - else + } else { err = check_stack_read(state, off, size, value_regno); + } } else { verbose("R%d invalid mem access '%s'\n", regno, reg_type_str[state->regs[regno].type]); @@ -775,8 +820,13 @@ static int check_func_arg(struct verifier_env *env, u32 regno, return -EACCES; } - if (arg_type == ARG_ANYTHING) + if (arg_type == ARG_ANYTHING) { + if (is_pointer_value(env, regno)) { + verbose("R%d leaks addr into helper function\n", regno); + return -EACCES; + } return 0; + } if (arg_type == ARG_PTR_TO_STACK || arg_type == ARG_PTR_TO_MAP_KEY || arg_type == ARG_PTR_TO_MAP_VALUE) { @@ -950,8 +1000,9 @@ static int check_call(struct verifier_env *env, int func_id) } /* check validity of 32-bit and 64-bit arithmetic operations */ -static int check_alu_op(struct reg_state *regs, struct bpf_insn *insn) +static int check_alu_op(struct verifier_env *env, struct bpf_insn *insn) { + struct reg_state *regs = env->cur_state.regs; u8 opcode = BPF_OP(insn->code); int err; @@ -976,6 +1027,12 @@ static int check_alu_op(struct reg_state *regs, struct bpf_insn *insn) if (err) return err; + if (is_pointer_value(env, insn->dst_reg)) { + verbose("R%d pointer arithmetic prohibited\n", + insn->dst_reg); + return -EACCES; + } + /* check dest operand */ err = check_reg_arg(regs, insn->dst_reg, DST_OP); if (err) @@ -1012,6 +1069,11 @@ static int check_alu_op(struct reg_state *regs, struct bpf_insn *insn) */ regs[insn->dst_reg] = regs[insn->src_reg]; } else { + if (is_pointer_value(env, insn->src_reg)) { + verbose("R%d partial copy of pointer\n", + insn->src_reg); + return -EACCES; + } regs[insn->dst_reg].type = UNKNOWN_VALUE; regs[insn->dst_reg].map_ptr = NULL; } @@ -1061,8 +1123,18 @@ static int check_alu_op(struct reg_state *regs, struct bpf_insn *insn) /* pattern match 'bpf_add Rx, imm' instruction */ if (opcode == BPF_ADD && BPF_CLASS(insn->code) == BPF_ALU64 && regs[insn->dst_reg].type == FRAME_PTR && - BPF_SRC(insn->code) == BPF_K) + BPF_SRC(insn->code) == BPF_K) { stack_relative = true; + } else if (is_pointer_value(env, insn->dst_reg)) { + verbose("R%d pointer arithmetic prohibited\n", + insn->dst_reg); + return -EACCES; + } else if (BPF_SRC(insn->code) == BPF_X && + is_pointer_value(env, insn->src_reg)) { + verbose("R%d pointer arithmetic prohibited\n", + insn->src_reg); + return -EACCES; + } /* check dest operand */ err = check_reg_arg(regs, insn->dst_reg, DST_OP); @@ -1101,6 +1173,12 @@ static int check_cond_jmp_op(struct verifier_env *env, err = check_reg_arg(regs, insn->src_reg, SRC_OP); if (err) return err; + + if (is_pointer_value(env, insn->src_reg)) { + verbose("R%d pointer comparison prohibited\n", + insn->src_reg); + return -EACCES; + } } else { if (insn->src_reg != BPF_REG_0) { verbose("BPF_JMP uses reserved fields\n"); @@ -1155,6 +1233,9 @@ static int check_cond_jmp_op(struct verifier_env *env, regs[insn->dst_reg].type = CONST_IMM; regs[insn->dst_reg].imm = 0; } + } else if (is_pointer_value(env, insn->dst_reg)) { + verbose("R%d pointer comparison prohibited\n", insn->dst_reg); + return -EACCES; } else if (BPF_SRC(insn->code) == BPF_K && (opcode == BPF_JEQ || opcode == BPF_JNE)) { @@ -1658,7 +1739,7 @@ static int do_check(struct verifier_env *env) } if (class == BPF_ALU || class == BPF_ALU64) { - err = check_alu_op(regs, insn); + err = check_alu_op(env, insn); if (err) return err; @@ -1816,6 +1897,11 @@ static int do_check(struct verifier_env *env) if (err) return err; + if (is_pointer_value(env, BPF_REG_0)) { + verbose("R0 leaks addr as return value\n"); + return -EACCES; + } + process_bpf_exit: insn_idx = pop_stack(env, &prev_insn_idx); if (insn_idx < 0) { @@ -2144,6 +2230,8 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr) if (ret < 0) goto skip_full_check; + env->allow_ptr_leaks = capable(CAP_SYS_ADMIN); + ret = do_check(env); skip_full_check: diff --git a/kernel/sysctl.c b/kernel/sysctl.c index e69201d..96c856b 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -64,6 +64,7 @@ #include #include #include +#include #include #include @@ -1139,6 +1140,18 @@ static struct ctl_table kern_table[] = { .proc_handler = timer_migration_handler, }, #endif +#ifdef CONFIG_BPF_SYSCALL + { + .procname = "unprivileged_bpf_disabled", + .data = &sysctl_unprivileged_bpf_disabled, + .maxlen = sizeof(sysctl_unprivileged_bpf_disabled), + .mode = 0644, + /* only handle a transition from default "0" to "1" */ + .proc_handler = proc_dointvec_minmax, + .extra1 = &one, + .extra2 = &one, + }, +#endif { } }; diff --git a/net/core/filter.c b/net/core/filter.c index 5f4cf1c..0b00094 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1640,7 +1640,8 @@ sk_filter_func_proto(enum bpf_func_id func_id) case BPF_FUNC_ktime_get_ns: return &bpf_ktime_get_ns_proto; case BPF_FUNC_trace_printk: - return bpf_get_trace_printk_proto(); + if (capable(CAP_SYS_ADMIN)) + return bpf_get_trace_printk_proto(); default: return NULL; } -- cgit v0.10.2 From aaac3ba95e4c8b496d22f68bd1bc01cfbf525eca Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Wed, 7 Oct 2015 22:23:22 -0700 Subject: bpf: charge user for creation of BPF maps and programs since eBPF programs and maps use kernel memory consider it 'locked' memory from user accounting point of view and charge it against RLIMIT_MEMLOCK limit. This limit is typically set to 64Kbytes by distros, so almost all bpf+tracing programs would need to increase it, since they use maps, but kernel charges maximum map size upfront. For example the hash map of 1024 elements will be charged as 64Kbyte. It's inconvenient for current users and changes current behavior for root, but probably worth doing to be consistent root vs non-root. Similar accounting logic is done by mmap of perf_event. Signed-off-by: Alexei Starovoitov Signed-off-by: David S. Miller diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 02fa3db..e3a51b7 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -36,6 +36,8 @@ struct bpf_map { u32 key_size; u32 value_size; u32 max_entries; + u32 pages; + struct user_struct *user; const struct bpf_map_ops *ops; struct work_struct work; }; @@ -128,6 +130,7 @@ struct bpf_prog_aux { const struct bpf_verifier_ops *ops; struct bpf_map **used_maps; struct bpf_prog *prog; + struct user_struct *user; union { struct work_struct work; struct rcu_head rcu; diff --git a/include/linux/sched.h b/include/linux/sched.h index b7b9501..4817df5 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -840,7 +840,7 @@ struct user_struct { struct hlist_node uidhash_node; kuid_t uid; -#ifdef CONFIG_PERF_EVENTS +#if defined(CONFIG_PERF_EVENTS) || defined(CONFIG_BPF_SYSCALL) atomic_long_t locked_vm; #endif }; diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index 2fecc4a..f2d9e69 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -49,7 +49,7 @@ static struct bpf_map *array_map_alloc(union bpf_attr *attr) array->map.key_size = attr->key_size; array->map.value_size = attr->value_size; array->map.max_entries = attr->max_entries; - + array->map.pages = round_up(array_size, PAGE_SIZE) >> PAGE_SHIFT; array->elem_size = elem_size; return &array->map; diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index 83c209d..28592d7 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -88,6 +88,10 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr) htab->elem_size = sizeof(struct htab_elem) + round_up(htab->map.key_size, 8) + htab->map.value_size; + + htab->map.pages = round_up(htab->n_buckets * sizeof(struct hlist_head) + + htab->elem_size * htab->map.max_entries, + PAGE_SIZE) >> PAGE_SHIFT; return &htab->map; free_htab: diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 83697bc..f640e5f 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -46,11 +46,38 @@ void bpf_register_map_type(struct bpf_map_type_list *tl) list_add(&tl->list_node, &bpf_map_types); } +static int bpf_map_charge_memlock(struct bpf_map *map) +{ + struct user_struct *user = get_current_user(); + unsigned long memlock_limit; + + memlock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; + + atomic_long_add(map->pages, &user->locked_vm); + + if (atomic_long_read(&user->locked_vm) > memlock_limit) { + atomic_long_sub(map->pages, &user->locked_vm); + free_uid(user); + return -EPERM; + } + map->user = user; + return 0; +} + +static void bpf_map_uncharge_memlock(struct bpf_map *map) +{ + struct user_struct *user = map->user; + + atomic_long_sub(map->pages, &user->locked_vm); + free_uid(user); +} + /* called from workqueue */ static void bpf_map_free_deferred(struct work_struct *work) { struct bpf_map *map = container_of(work, struct bpf_map, work); + bpf_map_uncharge_memlock(map); /* implementation dependent freeing */ map->ops->map_free(map); } @@ -110,6 +137,10 @@ static int map_create(union bpf_attr *attr) atomic_set(&map->refcnt, 1); + err = bpf_map_charge_memlock(map); + if (err) + goto free_map; + err = anon_inode_getfd("bpf-map", &bpf_map_fops, map, O_RDWR | O_CLOEXEC); if (err < 0) @@ -442,11 +473,37 @@ static void free_used_maps(struct bpf_prog_aux *aux) kfree(aux->used_maps); } +static int bpf_prog_charge_memlock(struct bpf_prog *prog) +{ + struct user_struct *user = get_current_user(); + unsigned long memlock_limit; + + memlock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; + + atomic_long_add(prog->pages, &user->locked_vm); + if (atomic_long_read(&user->locked_vm) > memlock_limit) { + atomic_long_sub(prog->pages, &user->locked_vm); + free_uid(user); + return -EPERM; + } + prog->aux->user = user; + return 0; +} + +static void bpf_prog_uncharge_memlock(struct bpf_prog *prog) +{ + struct user_struct *user = prog->aux->user; + + atomic_long_sub(prog->pages, &user->locked_vm); + free_uid(user); +} + static void __prog_put_rcu(struct rcu_head *rcu) { struct bpf_prog_aux *aux = container_of(rcu, struct bpf_prog_aux, rcu); free_used_maps(aux); + bpf_prog_uncharge_memlock(aux->prog); bpf_prog_free(aux->prog); } @@ -554,6 +611,10 @@ static int bpf_prog_load(union bpf_attr *attr) if (!prog) return -ENOMEM; + err = bpf_prog_charge_memlock(prog); + if (err) + goto free_prog_nouncharge; + prog->len = attr->insn_cnt; err = -EFAULT; @@ -595,6 +656,8 @@ static int bpf_prog_load(union bpf_attr *attr) free_used_maps: free_used_maps(prog->aux); free_prog: + bpf_prog_uncharge_memlock(prog); +free_prog_nouncharge: bpf_prog_free(prog); return err; } -- cgit v0.10.2 From bf5088773faffc4a052b95aa978a1660bf5f3f8d Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Wed, 7 Oct 2015 22:23:23 -0700 Subject: bpf: add unprivileged bpf tests Add new tests samples/bpf/test_verifier: unpriv: return pointer checks that pointer cannot be returned from the eBPF program unpriv: add const to pointer unpriv: add pointer to pointer unpriv: neg pointer checks that pointer arithmetic is disallowed unpriv: cmp pointer with const unpriv: cmp pointer with pointer checks that comparison of pointers is disallowed Only one case allowed 'void *value = bpf_map_lookup_elem(..); if (value == 0) ...' unpriv: check that printk is disallowed since bpf_trace_printk is not available to unprivileged unpriv: pass pointer to helper function checks that pointers cannot be passed to functions that expect integers If function expects a pointer the verifier allows only that type of pointer. Like 1st argument of bpf_map_lookup_elem() must be pointer to map. (applies to non-root as well) unpriv: indirectly pass pointer on stack to helper function checks that pointer stored into stack cannot be used as part of key passed into bpf_map_lookup_elem() unpriv: mangle pointer on stack 1 unpriv: mangle pointer on stack 2 checks that writing into stack slot that already contains a pointer is disallowed unpriv: read pointer from stack in small chunks checks that < 8 byte read from stack slot that contains a pointer is disallowed unpriv: write pointer into ctx checks that storing pointers into skb->fields is disallowed unpriv: write pointer into map elem value checks that storing pointers into element values is disallowed For example: int bpf_prog(struct __sk_buff *skb) { u32 key = 0; u64 *value = bpf_map_lookup_elem(&map, &key); if (value) *value = (u64) skb; } will be rejected. unpriv: partial copy of pointer checks that doing 32-bit register mov from register containing a pointer is disallowed unpriv: pass pointer to tail_call checks that passing pointer as an index into bpf_tail_call is disallowed unpriv: cmp map pointer with zero checks that comparing map pointer with constant is disallowed unpriv: write into frame pointer checks that frame pointer is read-only (applies to root too) unpriv: cmp of frame pointer checks that R10 cannot be using in comparison unpriv: cmp of stack pointer checks that Rx = R10 - imm is ok, but comparing Rx is not unpriv: obfuscate stack pointer checks that Rx = R10 - imm is ok, but Rx -= imm is not Signed-off-by: Alexei Starovoitov Signed-off-by: David S. Miller diff --git a/samples/bpf/libbpf.h b/samples/bpf/libbpf.h index 7235e29..b7f63c70 100644 --- a/samples/bpf/libbpf.h +++ b/samples/bpf/libbpf.h @@ -64,6 +64,14 @@ extern char bpf_log_buf[LOG_BUF_SIZE]; .off = 0, \ .imm = 0 }) +#define BPF_MOV32_REG(DST, SRC) \ + ((struct bpf_insn) { \ + .code = BPF_ALU | BPF_MOV | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = 0 }) + /* Short form of mov, dst_reg = imm32 */ #define BPF_MOV64_IMM(DST, IMM) \ diff --git a/samples/bpf/test_verifier.c b/samples/bpf/test_verifier.c index ee0f110..563c507 100644 --- a/samples/bpf/test_verifier.c +++ b/samples/bpf/test_verifier.c @@ -15,20 +15,27 @@ #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[32]; + int fixup[MAX_FIXUPS]; + int prog_array_fixup[MAX_FIXUPS]; const char *errstr; + const char *errstr_unpriv; enum { + UNDEF, ACCEPT, REJECT - } result; + } result, result_unpriv; enum bpf_prog_type prog_type; }; @@ -96,6 +103,7 @@ static struct bpf_test tests[] = { BPF_EXIT_INSN(), }, .errstr = "invalid BPF_LD_IMM insn", + .errstr_unpriv = "R1 pointer comparison", .result = REJECT, }, { @@ -109,6 +117,7 @@ static struct bpf_test tests[] = { BPF_EXIT_INSN(), }, .errstr = "invalid BPF_LD_IMM insn", + .errstr_unpriv = "R1 pointer comparison", .result = REJECT, }, { @@ -219,6 +228,7 @@ static struct bpf_test tests[] = { BPF_EXIT_INSN(), }, .errstr = "R0 !read_ok", + .errstr_unpriv = "R1 pointer comparison", .result = REJECT, }, { @@ -294,7 +304,9 @@ static struct bpf_test tests[] = { BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), BPF_EXIT_INSN(), }, + .errstr_unpriv = "R0 leaks addr", .result = ACCEPT, + .result_unpriv = REJECT, }, { "check corrupted spill/fill", @@ -310,6 +322,7 @@ static struct bpf_test tests[] = { BPF_EXIT_INSN(), }, + .errstr_unpriv = "attempt to corrupt spilled", .errstr = "corrupted spill", .result = REJECT, }, @@ -473,6 +486,7 @@ static struct bpf_test tests[] = { }, .fixup = {3}, .errstr = "R0 invalid mem access", + .errstr_unpriv = "R0 leaks addr", .result = REJECT, }, { @@ -495,6 +509,8 @@ static struct bpf_test tests[] = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, + .errstr_unpriv = "R1 pointer comparison", + .result_unpriv = REJECT, .result = ACCEPT, }, { @@ -521,6 +537,8 @@ static struct bpf_test tests[] = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, + .errstr_unpriv = "R1 pointer comparison", + .result_unpriv = REJECT, .result = ACCEPT, }, { @@ -555,6 +573,8 @@ static struct bpf_test tests[] = { BPF_EXIT_INSN(), }, .fixup = {24}, + .errstr_unpriv = "R1 pointer comparison", + .result_unpriv = REJECT, .result = ACCEPT, }, { @@ -603,6 +623,8 @@ static struct bpf_test tests[] = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, + .errstr_unpriv = "R1 pointer comparison", + .result_unpriv = REJECT, .result = ACCEPT, }, { @@ -642,6 +664,8 @@ static struct bpf_test tests[] = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, + .errstr_unpriv = "R1 pointer comparison", + .result_unpriv = REJECT, .result = ACCEPT, }, { @@ -699,6 +723,7 @@ static struct bpf_test tests[] = { }, .fixup = {4}, .errstr = "different pointers", + .errstr_unpriv = "R1 pointer comparison", .result = REJECT, }, { @@ -720,6 +745,7 @@ static struct bpf_test tests[] = { }, .fixup = {6}, .errstr = "different pointers", + .errstr_unpriv = "R1 pointer comparison", .result = REJECT, }, { @@ -742,6 +768,7 @@ static struct bpf_test tests[] = { }, .fixup = {7}, .errstr = "different pointers", + .errstr_unpriv = "R1 pointer comparison", .result = REJECT, }, { @@ -752,6 +779,7 @@ static struct bpf_test tests[] = { BPF_EXIT_INSN(), }, .errstr = "invalid bpf_context access", + .errstr_unpriv = "R1 leaks addr", .result = REJECT, }, { @@ -762,6 +790,7 @@ static struct bpf_test tests[] = { BPF_EXIT_INSN(), }, .errstr = "invalid bpf_context access", + .errstr_unpriv = "R1 leaks addr", .result = REJECT, }, { @@ -772,16 +801,18 @@ static struct bpf_test tests[] = { 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[60])), + 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, }, @@ -803,6 +834,8 @@ static struct bpf_test tests[] = { BPF_EXIT_INSN(), }, .result = ACCEPT, + .errstr_unpriv = "R1 leaks addr", + .result_unpriv = REJECT, }, { "write skb fields from tc_cls_act prog", @@ -819,6 +852,8 @@ static struct bpf_test tests[] = { offsetof(struct __sk_buff, cb[3])), BPF_EXIT_INSN(), }, + .errstr_unpriv = "", + .result_unpriv = REJECT, .result = ACCEPT, .prog_type = BPF_PROG_TYPE_SCHED_CLS, }, @@ -881,6 +916,270 @@ static struct bpf_test tests[] = { .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: 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: 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, + }, }; static int probe_filter_length(struct bpf_insn *fp) @@ -896,13 +1195,24 @@ static int probe_filter_length(struct bpf_insn *fp) static int create_map(void) { - long long key, value = 0; int map_fd; - map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), 1024); - if (map_fd < 0) { + map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, + sizeof(long long), sizeof(long long), 1024); + 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); + if (map_fd < 0) + printf("failed to create prog_array '%s'\n", strerror(errno)); return map_fd; } @@ -910,13 +1220,17 @@ static int create_map(void) 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 map_fd = -1; + int *prog_array_fixup = tests[i].prog_array_fixup; + int expected_result; + const char *expected_errstr; + int map_fd = -1, prog_array_fd = -1; if (*fixup) { map_fd = create_map(); @@ -926,13 +1240,31 @@ static int test(void) 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); + } 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 (tests[i].result == ACCEPT) { + 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)); @@ -947,7 +1279,7 @@ static int test(void) err_cnt++; goto fail; } - if (strstr(bpf_log_buf, tests[i].errstr) == 0) { + if (strstr(bpf_log_buf, expected_errstr) == 0) { printf("FAIL\nunexpected error message: %s", bpf_log_buf); err_cnt++; @@ -960,6 +1292,8 @@ static int test(void) fail: if (map_fd >= 0) close(map_fd); + if (prog_array_fd >= 0) + close(prog_array_fd); close(prog_fd); } @@ -970,5 +1304,8 @@ fail: int main(void) { + struct rlimit r = {1 << 20, 1 << 20}; + + setrlimit(RLIMIT_MEMLOCK, &r); return test(); } -- cgit v0.10.2 From f28ea365cdefc3b4fd0373e70b0106a0cd9b4c23 Mon Sep 17 00:00:00 2001 From: Edward Jee Date: Thu, 8 Oct 2015 14:56:48 -0700 Subject: sock: support per-packet fwmark It's useful to allow users to set fwmark for an individual packet, without changing the socket state. The function this patch adds in sock layer can be used by the protocols that need such a feature. Signed-off-by: Edward Hyunkoo Jee Signed-off-by: Eric Dumazet Cc: Willem de Bruijn Signed-off-by: David S. Miller diff --git a/include/net/sock.h b/include/net/sock.h index 771ca19..9322cafd 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1514,6 +1514,13 @@ void sock_kfree_s(struct sock *sk, void *mem, int size); void sock_kzfree_s(struct sock *sk, void *mem, int size); void sk_send_sigurg(struct sock *sk); +struct sockcm_cookie { + u32 mark; +}; + +int sock_cmsg_send(struct sock *sk, struct msghdr *msg, + struct sockcm_cookie *sockc); + /* * Functions to fill in entries in struct proto_ops when a protocol * does not implement a particular function. diff --git a/net/core/sock.c b/net/core/sock.c index 7dd1263..3395777 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1852,6 +1852,32 @@ struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size, } EXPORT_SYMBOL(sock_alloc_send_skb); +int sock_cmsg_send(struct sock *sk, struct msghdr *msg, + struct sockcm_cookie *sockc) +{ + struct cmsghdr *cmsg; + + for_each_cmsghdr(cmsg, msg) { + if (!CMSG_OK(msg, cmsg)) + return -EINVAL; + if (cmsg->cmsg_level != SOL_SOCKET) + continue; + switch (cmsg->cmsg_type) { + case SO_MARK: + if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) + return -EPERM; + if (cmsg->cmsg_len != CMSG_LEN(sizeof(u32))) + return -EINVAL; + sockc->mark = *(u32 *)CMSG_DATA(cmsg); + break; + default: + return -EINVAL; + } + } + return 0; +} +EXPORT_SYMBOL(sock_cmsg_send); + /* On 32bit arches, an skb frag is limited to 2^15 */ #define SKB_FRAG_PAGE_ORDER get_order(32768) -- cgit v0.10.2 From c7d39e32632e5db9dc4da51198b76d8c315946ff Mon Sep 17 00:00:00 2001 From: Edward Jee Date: Thu, 8 Oct 2015 14:56:49 -0700 Subject: packet: support per-packet fwmark for af_packet sendmsg Signed-off-by: Edward Hyunkoo Jee Signed-off-by: Eric Dumazet Cc: Willem de Bruijn Signed-off-by: David S. Miller diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 104910f..20c44e2 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -2630,6 +2630,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) __be16 proto; unsigned char *addr; int err, reserve = 0; + struct sockcm_cookie sockc; struct virtio_net_hdr vnet_hdr = { 0 }; int offset = 0; int vnet_hdr_len; @@ -2665,6 +2666,13 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) if (unlikely(!(dev->flags & IFF_UP))) goto out_unlock; + sockc.mark = sk->sk_mark; + if (msg->msg_controllen) { + err = sock_cmsg_send(sk, msg, &sockc); + if (unlikely(err)) + goto out_unlock; + } + if (sock->type == SOCK_RAW) reserve = dev->hard_header_len; if (po->has_vnet_hdr) { @@ -2774,7 +2782,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) skb->protocol = proto; skb->dev = dev; skb->priority = sk->sk_priority; - skb->mark = sk->sk_mark; + skb->mark = sockc.mark; packet_pick_tx_queue(dev, skb); -- cgit v0.10.2 From 70da268b569d32a9fddeea85dc18043de9d89f89 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 8 Oct 2015 19:33:21 -0700 Subject: net: SO_INCOMING_CPU setsockopt() support SO_INCOMING_CPU as added in commit 2c8c56e15df3 was a getsockopt() command to fetch incoming cpu handling a particular TCP flow after accept() This commits adds setsockopt() support and extends SO_REUSEPORT selection logic : If a TCP listener or UDP socket has this option set, a packet is delivered to this socket only if CPU handling the packet matches the specified one. This allows to build very efficient TCP servers, using one listener per RX queue, as the associated TCP listener should only accept flows handled in softirq by the same cpu. This provides optimal NUMA behavior and keep cpu caches hot. Note that __inet_lookup_listener() still has to iterate over the list of all listeners. Following patch puts sk_refcnt in a different cache line to let this iteration hit only shared and read mostly cache lines. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/include/net/sock.h b/include/net/sock.h index 9322cafd..cf54739 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -150,6 +150,7 @@ typedef __u64 __bitwise __addrpair; * @skc_node: main hash linkage for various protocol lookup tables * @skc_nulls_node: main hash linkage for TCP/UDP/UDP-Lite protocol * @skc_tx_queue_mapping: tx queue number for this connection + * @skc_incoming_cpu: record/match cpu processing incoming packets * @skc_refcnt: reference count * * This is the minimal network layer representation of sockets, the header @@ -212,6 +213,8 @@ struct sock_common { struct hlist_nulls_node skc_nulls_node; }; int skc_tx_queue_mapping; + int skc_incoming_cpu; + atomic_t skc_refcnt; /* private: */ int skc_dontcopy_end[0]; @@ -274,7 +277,6 @@ struct cg_proto; * @sk_rcvtimeo: %SO_RCVTIMEO setting * @sk_sndtimeo: %SO_SNDTIMEO setting * @sk_rxhash: flow hash received from netif layer - * @sk_incoming_cpu: record cpu processing incoming packets * @sk_txhash: computed flow hash for use on transmit * @sk_filter: socket filtering instructions * @sk_timer: sock cleanup timer @@ -331,6 +333,7 @@ struct sock { #define sk_v6_daddr __sk_common.skc_v6_daddr #define sk_v6_rcv_saddr __sk_common.skc_v6_rcv_saddr #define sk_cookie __sk_common.skc_cookie +#define sk_incoming_cpu __sk_common.skc_incoming_cpu socket_lock_t sk_lock; struct sk_buff_head sk_receive_queue; @@ -353,11 +356,6 @@ struct sock { #ifdef CONFIG_RPS __u32 sk_rxhash; #endif - u16 sk_incoming_cpu; - /* 16bit hole - * Warned : sk_incoming_cpu can be set from softirq, - * Do not use this hole without fully understanding possible issues. - */ __u32 sk_txhash; #ifdef CONFIG_NET_RX_BUSY_POLL diff --git a/net/core/sock.c b/net/core/sock.c index 3395777..dcc7d62 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -988,6 +988,10 @@ set_rcvbuf: sk->sk_max_pacing_rate); break; + case SO_INCOMING_CPU: + sk->sk_incoming_cpu = val; + break; + default: ret = -ENOPROTOOPT; break; @@ -2379,6 +2383,7 @@ void sock_init_data(struct socket *sock, struct sock *sk) sk->sk_max_pacing_rate = ~0U; sk->sk_pacing_rate = ~0U; + sk->sk_incoming_cpu = -1; /* * Before updating sk_refcnt, we must commit prior changes to memory * (Documentation/RCU/rculist_nulls.txt for details) diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index bed8886..08643a3 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -185,6 +185,8 @@ static inline int compute_score(struct sock *sk, struct net *net, return -1; score += 4; } + if (sk->sk_incoming_cpu == raw_smp_processor_id()) + score++; } return score; } diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index e1fc129..24ec14f9 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -375,7 +375,8 @@ static inline int compute_score(struct sock *sk, struct net *net, return -1; score += 4; } - + if (sk->sk_incoming_cpu == raw_smp_processor_id()) + score++; return score; } @@ -419,6 +420,9 @@ static inline int compute_score2(struct sock *sk, struct net *net, score += 4; } + if (sk->sk_incoming_cpu == raw_smp_processor_id()) + score++; + return score; } diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c index 6ac8dad..21ace5a 100644 --- a/net/ipv6/inet6_hashtables.c +++ b/net/ipv6/inet6_hashtables.c @@ -114,6 +114,8 @@ static inline int compute_score(struct sock *sk, struct net *net, return -1; score++; } + if (sk->sk_incoming_cpu == raw_smp_processor_id()) + score++; } return score; } diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 0aba654..01bcb49 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -182,10 +182,12 @@ static inline int compute_score(struct sock *sk, struct net *net, score++; } + if (sk->sk_incoming_cpu == raw_smp_processor_id()) + score++; + return score; } -#define SCORE2_MAX (1 + 1 + 1) static inline int compute_score2(struct sock *sk, struct net *net, const struct in6_addr *saddr, __be16 sport, const struct in6_addr *daddr, @@ -223,6 +225,9 @@ static inline int compute_score2(struct sock *sk, struct net *net, score++; } + if (sk->sk_incoming_cpu == raw_smp_processor_id()) + score++; + return score; } @@ -251,8 +256,7 @@ begin: hash = udp6_ehashfn(net, daddr, hnum, saddr, sport); matches = 1; - } else if (score == SCORE2_MAX) - goto exact_match; + } } else if (score == badness && reuseport) { matches++; if (reciprocal_scale(hash, matches) == 0) @@ -269,7 +273,6 @@ begin: goto begin; if (result) { -exact_match: if (unlikely(!atomic_inc_not_zero_hint(&result->sk_refcnt, 2))) result = NULL; else if (unlikely(compute_score2(result, net, saddr, sport, -- cgit v0.10.2 From 8e5eb54d303b7cb1174977ca79030e135728c95e Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 8 Oct 2015 19:33:22 -0700 Subject: net: align sk_refcnt on 128 bytes boundary sk->sk_refcnt is dirtied for every TCP/UDP incoming packet. This is a performance issue if multiple cpus hit a common socket, or multiple sockets are chained due to SO_REUSEPORT. By moving sk_refcnt 8 bytes further, first 128 bytes of sockets are mostly read. As they contain the lookup keys, this has a considerable performance impact, as cpus can cache them. These 8 bytes are not wasted, we use them as a place holder for various fields, depending on the socket type. Tested: SYN flood hitting a 16 RX queues NIC. TCP listener using 16 sockets and SO_REUSEPORT and SO_INCOMING_CPU for proper siloing. Could process 6.0 Mpps SYN instead of 4.2 Mpps Kernel profile looked like : 11.68% [kernel] [k] sha_transform 6.51% [kernel] [k] __inet_lookup_listener 5.07% [kernel] [k] __inet_lookup_established 4.15% [kernel] [k] memcpy_erms 3.46% [kernel] [k] ipt_do_table 2.74% [kernel] [k] fib_table_lookup 2.54% [kernel] [k] tcp_make_synack 2.34% [kernel] [k] tcp_conn_request 2.05% [kernel] [k] __netif_receive_skb_core 2.03% [kernel] [k] kmem_cache_alloc Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/include/net/inet_timewait_sock.h b/include/net/inet_timewait_sock.h index 186f3a1..e581fc6 100644 --- a/include/net/inet_timewait_sock.h +++ b/include/net/inet_timewait_sock.h @@ -70,6 +70,7 @@ struct inet_timewait_sock { #define tw_dport __tw_common.skc_dport #define tw_num __tw_common.skc_num #define tw_cookie __tw_common.skc_cookie +#define tw_dr __tw_common.skc_tw_dr int tw_timeout; volatile unsigned char tw_substate; @@ -88,7 +89,6 @@ struct inet_timewait_sock { kmemcheck_bitfield_end(flags); struct timer_list tw_timer; struct inet_bind_bucket *tw_tb; - struct inet_timewait_death_row *tw_dr; }; #define tw_tclass tw_tos diff --git a/include/net/request_sock.h b/include/net/request_sock.h index 95ab5d7..6b818b7 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -50,9 +50,9 @@ struct request_sock { struct sock_common __req_common; #define rsk_refcnt __req_common.skc_refcnt #define rsk_hash __req_common.skc_hash +#define rsk_listener __req_common.skc_listener struct request_sock *dl_next; - struct sock *rsk_listener; u16 mss; u8 num_retrans; /* number of retransmits */ u8 cookie_ts:1; /* syncookie: encode tcpopts in timestamp */ diff --git a/include/net/sock.h b/include/net/sock.h index cf54739..6571240 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -150,6 +150,9 @@ typedef __u64 __bitwise __addrpair; * @skc_node: main hash linkage for various protocol lookup tables * @skc_nulls_node: main hash linkage for TCP/UDP/UDP-Lite protocol * @skc_tx_queue_mapping: tx queue number for this connection + * @skc_flags: place holder for sk_flags + * %SO_LINGER (l_onoff), %SO_BROADCAST, %SO_KEEPALIVE, + * %SO_OOBINLINE settings, %SO_TIMESTAMPING settings * @skc_incoming_cpu: record/match cpu processing incoming packets * @skc_refcnt: reference count * @@ -201,6 +204,16 @@ struct sock_common { atomic64_t skc_cookie; + /* following fields are padding to force + * offset(struct sock, sk_refcnt) == 128 on 64bit arches + * assuming IPV6 is enabled. We use this padding differently + * for different kind of 'sockets' + */ + union { + unsigned long skc_flags; + struct sock *skc_listener; /* request_sock */ + struct inet_timewait_death_row *skc_tw_dr; /* inet_timewait_sock */ + }; /* * fields between dontcopy_begin/dontcopy_end * are not copied in sock_copy() @@ -246,8 +259,6 @@ struct cg_proto; * @sk_pacing_rate: Pacing rate (if supported by transport/packet scheduler) * @sk_max_pacing_rate: Maximum pacing rate (%SO_MAX_PACING_RATE) * @sk_sndbuf: size of send buffer in bytes - * @sk_flags: %SO_LINGER (l_onoff), %SO_BROADCAST, %SO_KEEPALIVE, - * %SO_OOBINLINE settings, %SO_TIMESTAMPING settings * @sk_no_check_tx: %SO_NO_CHECK setting, set checksum in TX packets * @sk_no_check_rx: allow zero checksum in RX packets * @sk_route_caps: route capabilities (e.g. %NETIF_F_TSO) @@ -334,6 +345,7 @@ struct sock { #define sk_v6_rcv_saddr __sk_common.skc_v6_rcv_saddr #define sk_cookie __sk_common.skc_cookie #define sk_incoming_cpu __sk_common.skc_incoming_cpu +#define sk_flags __sk_common.skc_flags socket_lock_t sk_lock; struct sk_buff_head sk_receive_queue; @@ -371,7 +383,6 @@ struct sock { #ifdef CONFIG_XFRM struct xfrm_policy *sk_policy[2]; #endif - unsigned long sk_flags; struct dst_entry *sk_rx_dst; struct dst_entry __rcu *sk_dst_cache; spinlock_t sk_dst_lock; -- cgit v0.10.2 From ed53d0ab761f5c71d77c8dc05fd19c0a851200db Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 8 Oct 2015 19:33:23 -0700 Subject: net: shrink struct sock and request_sock by 8 bytes One 32bit hole is following skc_refcnt, use it. skc_incoming_cpu can also be an union for request_sock rcv_wnd. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/include/net/request_sock.h b/include/net/request_sock.h index 6b818b7..2e73748 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -51,15 +51,14 @@ struct request_sock { #define rsk_refcnt __req_common.skc_refcnt #define rsk_hash __req_common.skc_hash #define rsk_listener __req_common.skc_listener +#define rsk_window_clamp __req_common.skc_window_clamp +#define rsk_rcv_wnd __req_common.skc_rcv_wnd struct request_sock *dl_next; u16 mss; u8 num_retrans; /* number of retransmits */ u8 cookie_ts:1; /* syncookie: encode tcpopts in timestamp */ u8 num_timeout:7; /* number of timeouts */ - /* The following two fields can be easily recomputed I think -AK */ - u32 window_clamp; /* window clamp at creation time */ - u32 rcv_wnd; /* rcv_wnd offered first time */ u32 ts_recent; struct timer_list rsk_timer; const struct request_sock_ops *rsk_ops; diff --git a/include/net/sock.h b/include/net/sock.h index 6571240..19cfe1f 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -226,11 +226,18 @@ struct sock_common { struct hlist_nulls_node skc_nulls_node; }; int skc_tx_queue_mapping; - int skc_incoming_cpu; + union { + int skc_incoming_cpu; + u32 skc_rcv_wnd; + }; atomic_t skc_refcnt; /* private: */ int skc_dontcopy_end[0]; + union { + u32 skc_rxhash; + u32 skc_window_clamp; + }; /* public: */ }; @@ -287,7 +294,6 @@ struct cg_proto; * @sk_rcvlowat: %SO_RCVLOWAT setting * @sk_rcvtimeo: %SO_RCVTIMEO setting * @sk_sndtimeo: %SO_SNDTIMEO setting - * @sk_rxhash: flow hash received from netif layer * @sk_txhash: computed flow hash for use on transmit * @sk_filter: socket filtering instructions * @sk_timer: sock cleanup timer @@ -346,6 +352,7 @@ struct sock { #define sk_cookie __sk_common.skc_cookie #define sk_incoming_cpu __sk_common.skc_incoming_cpu #define sk_flags __sk_common.skc_flags +#define sk_rxhash __sk_common.skc_rxhash socket_lock_t sk_lock; struct sk_buff_head sk_receive_queue; @@ -365,9 +372,6 @@ struct sock { } sk_backlog; #define sk_rmem_alloc sk_backlog.rmem_alloc int sk_forward_alloc; -#ifdef CONFIG_RPS - __u32 sk_rxhash; -#endif __u32 sk_txhash; #ifdef CONFIG_NET_RX_BUSY_POLL diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 2dbb113..4c0892b 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -382,10 +382,10 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) } /* Try to redo what tcp_v4_send_synack did. */ - req->window_clamp = tp->window_clamp ? :dst_metric(&rt->dst, RTAX_WINDOW); + req->rsk_window_clamp = tp->window_clamp ? :dst_metric(&rt->dst, RTAX_WINDOW); tcp_select_initial_window(tcp_full_space(sk), req->mss, - &req->rcv_wnd, &req->window_clamp, + &req->rsk_rcv_wnd, &req->rsk_window_clamp, ireq->wscale_ok, &rcv_wscale, dst_metric(&rt->dst, RTAX_INITRWND)); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index ddadb31..3b35c3f 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6022,7 +6022,7 @@ static void tcp_openreq_init(struct request_sock *req, { struct inet_request_sock *ireq = inet_rsk(req); - req->rcv_wnd = 0; /* So that tcp_send_synack() knows! */ + req->rsk_rcv_wnd = 0; /* So that tcp_send_synack() knows! */ req->cookie_ts = 0; tcp_rsk(req)->rcv_isn = TCP_SKB_CB(skb)->seq; tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->seq + 1; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 3431074..ddb1983 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -803,7 +803,7 @@ static void tcp_v4_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb, */ tcp_v4_send_ack(skb, (sk->sk_state == TCP_LISTEN) ? tcp_rsk(req)->snt_isn + 1 : tcp_sk(sk)->snd_nxt, - tcp_rsk(req)->rcv_nxt, req->rcv_wnd, + tcp_rsk(req)->rcv_nxt, req->rsk_rcv_wnd, tcp_time_stamp, req->ts_recent, 0, diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 1079e6a..41828bd 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -381,18 +381,18 @@ void tcp_openreq_init_rwin(struct request_sock *req, window_clamp = READ_ONCE(tp->window_clamp); /* Set this up on the first call only */ - req->window_clamp = window_clamp ? : dst_metric(dst, RTAX_WINDOW); + req->rsk_window_clamp = window_clamp ? : dst_metric(dst, RTAX_WINDOW); /* limit the window selection if the user enforce a smaller rx buffer */ if (sk_listener->sk_userlocks & SOCK_RCVBUF_LOCK && - (req->window_clamp > full_space || req->window_clamp == 0)) - req->window_clamp = full_space; + (req->rsk_window_clamp > full_space || req->rsk_window_clamp == 0)) + req->rsk_window_clamp = full_space; /* tcp_full_space because it is guaranteed to be the first packet */ tcp_select_initial_window(full_space, mss - (ireq->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0), - &req->rcv_wnd, - &req->window_clamp, + &req->rsk_rcv_wnd, + &req->rsk_window_clamp, ireq->wscale_ok, &rcv_wscale, dst_metric(dst, RTAX_INITRWND)); @@ -512,9 +512,9 @@ struct sock *tcp_create_openreq_child(const struct sock *sk, if (sysctl_tcp_fack) tcp_enable_fack(newtp); } - newtp->window_clamp = req->window_clamp; - newtp->rcv_ssthresh = req->rcv_wnd; - newtp->rcv_wnd = req->rcv_wnd; + newtp->window_clamp = req->rsk_window_clamp; + newtp->rcv_ssthresh = req->rsk_rcv_wnd; + newtp->rcv_wnd = req->rsk_rcv_wnd; newtp->rx_opt.wscale_ok = ireq->wscale_ok; if (newtp->rx_opt.wscale_ok) { newtp->rx_opt.snd_wscale = ireq->snd_wscale; @@ -707,7 +707,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, /* RFC793: "first check sequence number". */ if (paws_reject || !tcp_in_window(TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq, - tcp_rsk(req)->rcv_nxt, tcp_rsk(req)->rcv_nxt + req->rcv_wnd)) { + tcp_rsk(req)->rcv_nxt, tcp_rsk(req)->rcv_nxt + req->rsk_rcv_wnd)) { /* Out of window: send ACK and drop. */ if (!(flg & TCP_FLAG_RST)) req->rsk_ops->send_ack(sk, skb, req); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 55ed3266..6e79fcb 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -3023,7 +3023,7 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst, th->ack_seq = htonl(tcp_rsk(req)->rcv_nxt); /* RFC1323: The window in SYN & SYN/ACK segments is never scaled. */ - th->window = htons(min(req->rcv_wnd, 65535U)); + th->window = htons(min(req->rsk_rcv_wnd, 65535U)); tcp_options_write((__be32 *)(th + 1), NULL, &opts); th->doff = (tcp_header_size >> 2); TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_OUTSEGS); diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index f610b53..bb8f2fa 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -235,9 +235,9 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) goto out_free; } - req->window_clamp = tp->window_clamp ? :dst_metric(dst, RTAX_WINDOW); + req->rsk_window_clamp = tp->window_clamp ? :dst_metric(dst, RTAX_WINDOW); tcp_select_initial_window(tcp_full_space(sk), req->mss, - &req->rcv_wnd, &req->window_clamp, + &req->rsk_rcv_wnd, &req->rsk_window_clamp, ireq->wscale_ok, &rcv_wscale, dst_metric(dst, RTAX_INITRWND)); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 33334f0..2887c84 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -931,7 +931,7 @@ static void tcp_v6_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb, */ tcp_v6_send_ack(sk, skb, (sk->sk_state == TCP_LISTEN) ? tcp_rsk(req)->snt_isn + 1 : tcp_sk(sk)->snd_nxt, - tcp_rsk(req)->rcv_nxt, req->rcv_wnd, + tcp_rsk(req)->rcv_nxt, req->rsk_rcv_wnd, tcp_time_stamp, req->ts_recent, sk->sk_bound_dev_if, tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr), 0, 0); -- cgit v0.10.2 From d475f090bf1c0dc2999e98bbf2e7cb2243358849 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 8 Oct 2015 19:33:24 -0700 Subject: tcp: shrink tcp_timewait_sock by 8 bytes Reducing tcp_timewait_sock from 280 bytes to 272 bytes allows SLAB to pack 15 objects per page instead of 14 (on x86) Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/include/linux/tcp.h b/include/linux/tcp.h index e442e6e..86a7eda 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -356,8 +356,8 @@ static inline struct tcp_sock *tcp_sk(const struct sock *sk) struct tcp_timewait_sock { struct inet_timewait_sock tw_sk; - u32 tw_rcv_nxt; - u32 tw_snd_nxt; +#define tw_rcv_nxt tw_sk.__tw_common.skc_tw_rcv_nxt +#define tw_snd_nxt tw_sk.__tw_common.skc_tw_snd_nxt u32 tw_rcv_wnd; u32 tw_ts_offset; u32 tw_ts_recent; diff --git a/include/net/sock.h b/include/net/sock.h index 19cfe1f..64a7545 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -229,6 +229,7 @@ struct sock_common { union { int skc_incoming_cpu; u32 skc_rcv_wnd; + u32 skc_tw_rcv_nxt; /* struct tcp_timewait_sock */ }; atomic_t skc_refcnt; @@ -237,6 +238,7 @@ struct sock_common { union { u32 skc_rxhash; u32 skc_window_clamp; + u32 skc_tw_snd_nxt; /* struct tcp_timewait_sock */ }; /* public: */ }; -- cgit v0.10.2 From 3703ebe4035044dea3749fcdd1c0849937147959 Mon Sep 17 00:00:00 2001 From: wangweidong Date: Tue, 13 Oct 2015 10:05:19 +0800 Subject: BNX2: free temp_stats_blk on error path In bnx2_init_board, missing free temp_stats_blk on error path when some operations do failed. Just add the 'kfree' operation. Signed-off-by: Wang Weidong Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index 6259064..8fc3f3c 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -8476,6 +8476,8 @@ err_out_disable: pci_disable_device(pdev); err_out: + kfree(bp->temp_stats_blk); + return rc; } -- cgit v0.10.2 From 0944d6b5a2fad9ba3b7abb2e94a6b7d40cd4a935 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 9 Oct 2015 13:54:11 +0200 Subject: bridge: try switchdev op first in __vlan_vid_add/del Some drivers need to implement both switchdev vlan ops and vid_add/kill ndos. For that to work in bridge code, we need to try switchdev op first when adding/deleting vlan id. Signed-off-by: Jiri Pirko Signed-off-by: Ido Schimmel Acked-by: Scott Feldman Signed-off-by: David S. Miller diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 7a95e31..ad7e4f6 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -72,28 +72,20 @@ static void __vlan_add_flags(struct net_bridge_vlan *v, u16 flags) static int __vlan_vid_add(struct net_device *dev, struct net_bridge *br, u16 vid, u16 flags) { - const struct net_device_ops *ops = dev->netdev_ops; + struct switchdev_obj_port_vlan v = { + .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, + .flags = flags, + .vid_begin = vid, + .vid_end = vid, + }; int err; - /* If driver uses VLAN ndo ops, use 8021q to install vid - * on device, otherwise try switchdev ops to install vid. + /* Try switchdev op first. In case it is not supported, fallback to + * 8021q add. */ - - if (ops->ndo_vlan_rx_add_vid) { - err = vlan_vid_add(dev, br->vlan_proto, vid); - } else { - struct switchdev_obj_port_vlan v = { - .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, - .flags = flags, - .vid_begin = vid, - .vid_end = vid, - }; - - err = switchdev_port_obj_add(dev, &v.obj); - if (err == -EOPNOTSUPP) - err = 0; - } - + err = switchdev_port_obj_add(dev, &v.obj); + if (err == -EOPNOTSUPP) + return vlan_vid_add(dev, br->vlan_proto, vid); return err; } @@ -122,27 +114,21 @@ static void __vlan_del_list(struct net_bridge_vlan *v) static int __vlan_vid_del(struct net_device *dev, struct net_bridge *br, u16 vid) { - const struct net_device_ops *ops = dev->netdev_ops; - int err = 0; + struct switchdev_obj_port_vlan v = { + .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, + .vid_begin = vid, + .vid_end = vid, + }; + int err; - /* If driver uses VLAN ndo ops, use 8021q to delete vid - * on device, otherwise try switchdev ops to delete vid. + /* Try switchdev op first. In case it is not supported, fallback to + * 8021q del. */ - - if (ops->ndo_vlan_rx_kill_vid) { + err = switchdev_port_obj_del(dev, &v.obj); + if (err == -EOPNOTSUPP) { vlan_vid_del(dev, br->vlan_proto, vid); - } else { - struct switchdev_obj_port_vlan v = { - .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, - .vid_begin = vid, - .vid_end = vid, - }; - - err = switchdev_port_obj_del(dev, &v.obj); - if (err == -EOPNOTSUPP) - err = 0; + return 0; } - return err; } -- cgit v0.10.2 From e2ca690b657f4ca5c204fcc6470d462b776d73b3 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Fri, 9 Oct 2015 14:34:31 +0200 Subject: ipv4/icmp: redirect messages can use the ingress daddr as source This patch allows configuring how the source address of ICMP redirect messages is selected; by default the old behaviour is retained, while setting icmp_redirects_use_orig_daddr force the usage of the destination address of the packet that caused the redirect. The new behaviour fits closely the RFC 5798 section 8.1.1, and fix the following scenario: Two machines are set up with VRRP to act as routers out of a subnet, they have IPs x.x.x.1/24 and x.x.x.2/24, with VRRP holding on to x.x.x.254/24. If a host in said subnet needs to get an ICMP redirect from the VRRP router, i.e. to reach a destination behind a different gateway, the source IP in the ICMP redirect is chosen as the primary IP on the interface that the packet arrived at, i.e. x.x.x.1 or x.x.x.2. The host will then ignore said redirect, due to RFC 1122 section 3.2.2.2, and will continue to use the wrong next-op. Signed-off-by: Paolo Abeni Signed-off-by: David S. Miller diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index ebe94f2..9983825 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -884,8 +884,8 @@ icmp_ignore_bogus_error_responses - BOOLEAN icmp_errors_use_inbound_ifaddr - BOOLEAN - If zero, icmp error messages are sent with the primary address of - the exiting interface. + If zero, icmp error messages except redirects are sent with the primary + address of the exiting interface. If non-zero, the message will be sent with the primary address of the interface that received the packet that caused the icmp error. @@ -897,8 +897,23 @@ icmp_errors_use_inbound_ifaddr - BOOLEAN then the primary address of the first non-loopback interface that has one will be used regardless of this setting. + The source address selection of icmp redirect messages is controlled by + icmp_errors_use_inbound_ifaddr. Default: 0 +icmp_redirects_use_orig_daddr - BOOLEAN + + If zero, icmp redirect messages are sent using the address specified for + other icmp errors by icmp_errors_use_inbound_ifaddr. + + If non-zero, the message will be sent with the destination address of + the packet that caused the icmp redirect. + This behaviour is the preferred one on VRRP routers (see RFC 5798 + section 8.1.1). + + Default: 0 + + igmp_max_memberships - INTEGER Change the maximum number of multicast groups we can subscribe to. Default: 20 diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index c68926b..46d336a 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -74,6 +74,7 @@ struct netns_ipv4 { int sysctl_icmp_ratelimit; int sysctl_icmp_ratemask; int sysctl_icmp_errors_use_inbound_ifaddr; + int sysctl_icmp_redirects_use_orig_daddr; struct local_ports ip_local_ports; diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 36e2697..f3c356b 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -659,7 +659,9 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) */ saddr = iph->daddr; - if (!(rt->rt_flags & RTCF_LOCAL)) { + if (!((type == ICMP_REDIRECT) && + net->ipv4.sysctl_icmp_redirects_use_orig_daddr) && + !(rt->rt_flags & RTCF_LOCAL)) { struct net_device *dev = NULL; rcu_read_lock(); @@ -1222,6 +1224,11 @@ static int __net_init icmp_sk_init(struct net *net) net->ipv4.sysctl_icmp_ratemask = 0x1818; net->ipv4.sysctl_icmp_errors_use_inbound_ifaddr = 0; + /* Control paramerer - use the daddr of originating packets as saddr + * in redirect messages? + */ + net->ipv4.sysctl_icmp_redirects_use_orig_daddr = 0; + return 0; fail: diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 894da3a..30a531c 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -818,6 +818,13 @@ static struct ctl_table ipv4_net_table[] = { .proc_handler = proc_dointvec }, { + .procname = "icmp_redirects_use_orig_daddr", + .data = &init_net.ipv4.sysctl_icmp_redirects_use_orig_daddr, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, + { .procname = "icmp_ratelimit", .data = &init_net.ipv4.sysctl_icmp_ratelimit, .maxlen = sizeof(int), -- cgit v0.10.2 From 161642e24fee40fba2c5bc2ceacc00d118a22d65 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 9 Oct 2015 11:29:32 -0700 Subject: packet: fix match_fanout_group() Recent TCP listener patches exposed a prior af_packet bug : match_fanout_group() blindly assumes it is always safe to cast sk to a packet socket to compare fanout with af_packet_priv But SYNACK packets can be sent while attached to request_sock, which are smaller than a "struct sock". We can read non existent memory and crash. Fixes: c0de08d04215 ("af_packet: don't emit packet on orig fanout group") Fixes: ca6fb0651883 ("tcp: attach SYNACK messages to request sockets instead of listener") Signed-off-by: Eric Dumazet Cc: Willem de Bruijn Cc: Eric Leblond Signed-off-by: David S. Miller diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 20c44e2..396b3f1 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1519,10 +1519,10 @@ static void __fanout_unlink(struct sock *sk, struct packet_sock *po) static bool match_fanout_group(struct packet_type *ptype, struct sock *sk) { - if (ptype->af_packet_priv == (void *)((struct packet_sock *)sk)->fanout) - return true; + if (sk->sk_family != PF_PACKET) + return false; - return false; + return ptype->af_packet_priv == pkt_sk(sk)->fanout; } static void fanout_init_data(struct packet_fanout *f) -- cgit v0.10.2 From 37fcbab61b8ecf75cb5fd81e5809b71c270f9632 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 9 Oct 2015 13:44:53 -0500 Subject: ipv4: Only compute net once in ip_call_ra_chain ip_call_ra_chain is called early in the forwarding chain from ip_forward and ip_mr_input, which makes skb->dev the correct expression to get the input network device and dev_net(skb->dev) a correct expression for the network namespace the packet is being processed in. Compute the network namespace and store it in a variable to make the code clearer. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 7cc9f7b..804b86f 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -157,6 +157,7 @@ bool ip_call_ra_chain(struct sk_buff *skb) u8 protocol = ip_hdr(skb)->protocol; struct sock *last = NULL; struct net_device *dev = skb->dev; + struct net *net = dev_net(dev); for (ra = rcu_dereference(ip_ra_chain); ra; ra = rcu_dereference(ra->next)) { struct sock *sk = ra->sk; @@ -167,7 +168,7 @@ bool ip_call_ra_chain(struct sk_buff *skb) if (sk && inet_sk(sk)->inet_num == protocol && (!sk->sk_bound_dev_if || sk->sk_bound_dev_if == dev->ifindex) && - net_eq(sock_net(sk), dev_net(dev))) { + net_eq(sock_net(sk), net)) { if (ip_is_fragment(ip_hdr(skb))) { if (ip_defrag(skb, IP_DEFRAG_CALL_RA_CHAIN)) return true; -- cgit v0.10.2 From 19bcf9f203c82c2028f5a0881b1f0690e3207190 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 9 Oct 2015 13:44:54 -0500 Subject: ipv4: Pass struct net into ip_defrag and ip_check_defrag The function ip_defrag is called on both the input and the output paths of the networking stack. In particular conntrack when it is tracking outbound packets from the local machine calls ip_defrag. So add a struct net parameter and stop making ip_defrag guess which network namespace it needs to defragment packets in. Signed-off-by: "Eric W. Biederman" Acked-by: Pablo Neira Ayuso Signed-off-by: David S. Miller diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 47da435..86f6c62 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -412,7 +412,7 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb) port = macvlan_port_get_rcu(skb->dev); if (is_multicast_ether_addr(eth->h_dest)) { - skb = ip_check_defrag(skb, IP_DEFRAG_MACVLAN); + skb = ip_check_defrag(dev_net(skb->dev), skb, IP_DEFRAG_MACVLAN); if (!skb) return RX_HANDLER_CONSUMED; eth = eth_hdr(skb); diff --git a/include/net/ip.h b/include/net/ip.h index 3c904a2..1a98f1c 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -506,11 +506,11 @@ static inline bool ip_defrag_user_in_between(u32 user, return user >= lower_bond && user <= upper_bond; } -int ip_defrag(struct sk_buff *skb, u32 user); +int ip_defrag(struct net *net, struct sk_buff *skb, u32 user); #ifdef CONFIG_INET -struct sk_buff *ip_check_defrag(struct sk_buff *skb, u32 user); +struct sk_buff *ip_check_defrag(struct net *net, struct sk_buff *skb, u32 user); #else -static inline struct sk_buff *ip_check_defrag(struct sk_buff *skb, u32 user) +static inline struct sk_buff *ip_check_defrag(struct net *net, struct sk_buff *skb, u32 user) { return skb; } diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 9772b78..5482745 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -654,11 +654,10 @@ out_fail: } /* Process an incoming IP datagram fragment. */ -int ip_defrag(struct sk_buff *skb, u32 user) +int ip_defrag(struct net *net, struct sk_buff *skb, u32 user) { struct net_device *dev = skb->dev ? : skb_dst(skb)->dev; int vif = l3mdev_master_ifindex_rcu(dev); - struct net *net = dev_net(dev); struct ipq *qp; IP_INC_STATS_BH(net, IPSTATS_MIB_REASMREQDS); @@ -683,7 +682,7 @@ int ip_defrag(struct sk_buff *skb, u32 user) } EXPORT_SYMBOL(ip_defrag); -struct sk_buff *ip_check_defrag(struct sk_buff *skb, u32 user) +struct sk_buff *ip_check_defrag(struct net *net, struct sk_buff *skb, u32 user) { struct iphdr iph; int netoff; @@ -712,7 +711,7 @@ struct sk_buff *ip_check_defrag(struct sk_buff *skb, u32 user) if (pskb_trim_rcsum(skb, netoff + len)) return skb; memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); - if (ip_defrag(skb, user)) + if (ip_defrag(net, skb, user)) return NULL; skb_clear_hash(skb); } diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 804b86f..b1209b6 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -170,7 +170,7 @@ bool ip_call_ra_chain(struct sk_buff *skb) sk->sk_bound_dev_if == dev->ifindex) && net_eq(sock_net(sk), net)) { if (ip_is_fragment(ip_hdr(skb))) { - if (ip_defrag(skb, IP_DEFRAG_CALL_RA_CHAIN)) + if (ip_defrag(net, skb, IP_DEFRAG_CALL_RA_CHAIN)) return true; } if (last) { @@ -247,14 +247,15 @@ int ip_local_deliver(struct sk_buff *skb) /* * Reassemble IP fragments. */ + struct net *net = dev_net(skb->dev); if (ip_is_fragment(ip_hdr(skb))) { - if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER)) + if (ip_defrag(net, skb, IP_DEFRAG_LOCAL_DELIVER)) return 0; } return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN, - dev_net(skb->dev), NULL, skb, skb->dev, NULL, + net, NULL, skb, skb->dev, NULL, ip_local_deliver_finish); } diff --git a/net/ipv4/netfilter/nf_defrag_ipv4.c b/net/ipv4/netfilter/nf_defrag_ipv4.c index b246346..bf25f45 100644 --- a/net/ipv4/netfilter/nf_defrag_ipv4.c +++ b/net/ipv4/netfilter/nf_defrag_ipv4.c @@ -22,14 +22,15 @@ #endif #include -static int nf_ct_ipv4_gather_frags(struct sk_buff *skb, u_int32_t user) +static int nf_ct_ipv4_gather_frags(struct net *net, struct sk_buff *skb, + u_int32_t user) { int err; skb_orphan(skb); local_bh_disable(); - err = ip_defrag(skb, user); + err = ip_defrag(net, skb, user); local_bh_enable(); if (!err) { @@ -85,7 +86,7 @@ static unsigned int ipv4_conntrack_defrag(void *priv, enum ip_defrag_users user = nf_ct_defrag_user(state->hook, skb); - if (nf_ct_ipv4_gather_frags(skb, user)) + if (nf_ct_ipv4_gather_frags(state->net, skb, user)) return NF_STOLEN; } return NF_ACCEPT; diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 37dd77a..07a791e 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -694,7 +694,7 @@ static inline int ip_vs_gather_frags(struct netns_ipvs *ipvs, int err; local_bh_disable(); - err = ip_defrag(skb, user); + err = ip_defrag(ipvs->net, skb, user); local_bh_enable(); if (!err) ip_send_check(ip_hdr(skb)); diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index eb759e3..cb76076 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -304,7 +304,7 @@ static int handle_fragments(struct net *net, struct sw_flow_key *key, int err; memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); - err = ip_defrag(skb, user); + err = ip_defrag(net, skb, user); if (err) return err; diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 396b3f1..691660b 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1439,17 +1439,17 @@ static int packet_rcv_fanout(struct sk_buff *skb, struct net_device *dev, { struct packet_fanout *f = pt->af_packet_priv; unsigned int num = READ_ONCE(f->num_members); + struct net *net = read_pnet(&f->net); struct packet_sock *po; unsigned int idx; - if (!net_eq(dev_net(dev), read_pnet(&f->net)) || - !num) { + if (!net_eq(dev_net(dev), net) || !num) { kfree_skb(skb); return 0; } if (fanout_has_flag(f, PACKET_FANOUT_FLAG_DEFRAG)) { - skb = ip_check_defrag(skb, IP_DEFRAG_AF_PACKET); + skb = ip_check_defrag(net, skb, IP_DEFRAG_AF_PACKET); if (!skb) return 0; } -- cgit v0.10.2 From b72775977c39dcd380777ff5ea8041fdf67ee382 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 9 Oct 2015 13:44:55 -0500 Subject: ipv6: Pass struct net into nf_ct_frag6_gather The function nf_ct_frag6_gather is called on both the input and the output paths of the networking stack. In particular ipv6_defrag which calls nf_ct_frag6_gather is called from both the the PRE_ROUTING chain on input and the LOCAL_OUT chain on output. The addition of a net parameter makes it explicit which network namespace the packets are being reassembled in, and removes the need for nf_ct_frag6_gather to guess. Signed-off-by: "Eric W. Biederman" Acked-by: Pablo Neira Ayuso Signed-off-by: David S. Miller diff --git a/include/net/netfilter/ipv6/nf_defrag_ipv6.h b/include/net/netfilter/ipv6/nf_defrag_ipv6.h index 27666d8..fb7da5b 100644 --- a/include/net/netfilter/ipv6/nf_defrag_ipv6.h +++ b/include/net/netfilter/ipv6/nf_defrag_ipv6.h @@ -5,7 +5,7 @@ void nf_defrag_ipv6_enable(void); int nf_ct_frag6_init(void); void nf_ct_frag6_cleanup(void); -struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb, u32 user); +struct sk_buff *nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user); void nf_ct_frag6_consume_orig(struct sk_buff *skb); struct inet_frags_ctl; diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 701cd2b..2fb86a9 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -563,12 +563,10 @@ find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff) return 0; } -struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb, u32 user) +struct sk_buff *nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user) { struct sk_buff *clone; struct net_device *dev = skb->dev; - struct net *net = skb_dst(skb) ? dev_net(skb_dst(skb)->dev) - : dev_net(skb->dev); struct frag_hdr *fhdr; struct frag_queue *fq; struct ipv6hdr *hdr; diff --git a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c index a99baf6..5173a89 100644 --- a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c +++ b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c @@ -63,7 +63,8 @@ static unsigned int ipv6_defrag(void *priv, return NF_ACCEPT; #endif - reasm = nf_ct_frag6_gather(skb, nf_ct6_defrag_user(state->hook, skb)); + reasm = nf_ct_frag6_gather(state->net, skb, + nf_ct6_defrag_user(state->hook, skb)); /* queued */ if (reasm == NULL) return NF_STOLEN; diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index cb76076..ad61426 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -315,7 +315,7 @@ static int handle_fragments(struct net *net, struct sw_flow_key *key, struct sk_buff *reasm; memset(IP6CB(skb), 0, sizeof(struct inet6_skb_parm)); - reasm = nf_ct_frag6_gather(skb, user); + reasm = nf_ct_frag6_gather(net, skb, user); if (!reasm) return -EINPROGRESS; -- cgit v0.10.2 From 5fcd2d8be43664f1b6a5add3d21a367411add9d4 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 9 Oct 2015 15:42:21 -0700 Subject: tun: use sk_fullsock() before reading sk->sk_tsflags timewait or request sockets are small and do not contain sk->sk_tsflags Without this fix, we might read garbage, and crash later in __skb_complete_tx_timestamp() -> sock_queue_err_skb() (These pseudo sockets do not have an error queue either) Fixes: ca6fb0651883 ("tcp: attach SYNACK messages to request sockets instead of listener") Signed-off-by: Eric Dumazet Cc: Willem de Bruijn Acked-by: Michael S. Tsirkin Signed-off-by: David S. Miller diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 976aa97..b1878fa 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -858,7 +858,7 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC))) goto drop; - if (skb->sk) { + if (skb->sk && sk_fullsock(skb->sk)) { sock_tx_timestamp(skb->sk, &skb_shinfo(skb)->tx_flags); sw_tx_timestamp(skb); } -- cgit v0.10.2 From 99dcc7dfb1960d5e2f1577ff3aad69dfeb6e9787 Mon Sep 17 00:00:00 2001 From: huangdaode Date: Sat, 10 Oct 2015 17:20:38 +0800 Subject: net: hns: fix the unknown phy_nterface_t type error This patch fix the building error reported by Jiri Pirko drivers/net/ethernet/hisilicon/hns/hnae.h:465:2: error: unknown type name 'phy_interface_t' phy_interface_t phy_if; ^ the full build log is on https://lists.01.org/pipermail/kbuild-all. Signed-off-by: huangdaode Signed-off-by: yankejian Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h index 5edd8cd..d4a1eb1 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.h +++ b/drivers/net/ethernet/hisilicon/hns/hnae.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #define HNAE_DRIVER_VERSION "1.3.0" -- cgit v0.10.2 From 8c5b83f0f255542b40a1273c32eb067ec00bb2b2 Mon Sep 17 00:00:00 2001 From: Roopa Prabhu Date: Sat, 10 Oct 2015 08:26:36 -0700 Subject: ipv6 route: use err pointers instead of returning pointer by reference This patch makes ip6_route_info_create return err pointer instead of returning the rt pointer by reference as suggested by Dave Signed-off-by: Roopa Prabhu Signed-off-by: David S. Miller diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 4320ddc..db5b54a 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1724,21 +1724,21 @@ static int ip6_convert_metrics(struct mx6_config *mxc, return -EINVAL; } -int ip6_route_info_create(struct fib6_config *cfg, struct rt6_info **rt_ret) +static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg) { - int err; struct net *net = cfg->fc_nlinfo.nl_net; struct rt6_info *rt = NULL; struct net_device *dev = NULL; struct inet6_dev *idev = NULL; struct fib6_table *table; int addr_type; + int err = -EINVAL; if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128) - return -EINVAL; + goto out; #ifndef CONFIG_IPV6_SUBTREES if (cfg->fc_src_len) - return -EINVAL; + goto out; #endif if (cfg->fc_ifindex) { err = -ENODEV; @@ -1958,9 +1958,7 @@ install_route: cfg->fc_nlinfo.nl_net = dev_net(dev); - *rt_ret = rt; - - return 0; + return rt; out: if (dev) dev_put(dev); @@ -1969,20 +1967,21 @@ out: if (rt) dst_free(&rt->dst); - *rt_ret = NULL; - - return err; + return ERR_PTR(err); } int ip6_route_add(struct fib6_config *cfg) { struct mx6_config mxc = { .mx = NULL, }; - struct rt6_info *rt = NULL; + struct rt6_info *rt; int err; - err = ip6_route_info_create(cfg, &rt); - if (err) + rt = ip6_route_info_create(cfg); + if (IS_ERR(rt)) { + err = PTR_ERR(rt); + rt = NULL; goto out; + } err = ip6_convert_metrics(&mxc, cfg); if (err) @@ -2871,9 +2870,12 @@ static int ip6_route_multipath_add(struct fib6_config *cfg) r_cfg.fc_encap_type = nla_get_u16(nla); } - err = ip6_route_info_create(&r_cfg, &rt); - if (err) + rt = ip6_route_info_create(&r_cfg); + if (IS_ERR(rt)) { + err = PTR_ERR(rt); + rt = NULL; goto cleanup; + } err = ip6_route_info_append(&rt6_nh_list, rt, &r_cfg); if (err) { -- cgit v0.10.2 From 21e26ff993dea9dceaf0f19cfec4bef58387b0f2 Mon Sep 17 00:00:00 2001 From: Tillmann Heidsieck Date: Sat, 10 Oct 2015 21:47:18 +0200 Subject: atm: iphase: return -ENOMEM instead of -1 in case of failed kmalloc() Smatch complains about returning hard coded error codes, silence this warning. drivers/atm/iphase.c:115 ia_enque_rtn_q() warn: returning -1 instead of -ENOMEM is sloppy Signed-off-by: Tillmann Heidsieck Signed-off-by: David S. Miller diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c index 65e6590..d5010d7 100644 --- a/drivers/atm/iphase.c +++ b/drivers/atm/iphase.c @@ -112,7 +112,8 @@ static void ia_enque_head_rtn_q (IARTN_Q *que, IARTN_Q * data) static int ia_enque_rtn_q (IARTN_Q *que, struct desc_tbl_t data) { IARTN_Q *entry = kmalloc(sizeof(*entry), GFP_ATOMIC); - if (!entry) return -1; + if (!entry) + return -ENOMEM; entry->data = data; entry->next = NULL; if (que->next == NULL) -- cgit v0.10.2 From cbb41b91e68a302087762823136c9067138cff7c Mon Sep 17 00:00:00 2001 From: Tillmann Heidsieck Date: Sat, 10 Oct 2015 21:47:19 +0200 Subject: atm: iphase: fix misleading indention Fix a smatch warning: drivers/atm/iphase.c:1178 rx_pkt() warn: curly braces intended? The code is correct, the indention is misleading. In case the allocation of skb fails, we want to skip to the end. Signed-off-by: Tillmann Heidsieck Signed-off-by: David S. Miller diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c index d5010d7..7d00f29 100644 --- a/drivers/atm/iphase.c +++ b/drivers/atm/iphase.c @@ -1176,7 +1176,7 @@ static int rx_pkt(struct atm_dev *dev) if (!(skb = atm_alloc_charge(vcc, len, GFP_ATOMIC))) { if (vcc->vci < 32) printk("Drop control packets\n"); - goto out_free_desc; + goto out_free_desc; } skb_put(skb,len); // pwang_test -- cgit v0.10.2 From 6623c60dc28ee966cd85c6f12aa2fc3c952d0179 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 11 Oct 2015 12:49:56 +0200 Subject: bridge: vlan: enforce no pvid flag in vlan ranges Currently it's possible for someone to send a vlan range to the kernel with the pvid flag set which will result in the pvid bouncing from a vlan to vlan and isn't correct, it also introduces problems for hardware where it doesn't make sense having more than 1 pvid. iproute2 already enforces this, so let's enforce it on kernel-side as well. Reported-by: Elad Raz Signed-off-by: Nikolay Aleksandrov Acked-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 544ab96..d792d1a 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -524,6 +524,9 @@ static int br_afspec(struct net_bridge *br, if (vinfo_start) return -EINVAL; vinfo_start = vinfo; + /* don't allow range of pvids */ + if (vinfo_start->flags & BRIDGE_VLAN_INFO_PVID) + return -EINVAL; continue; } -- cgit v0.10.2 From 571f2c11b343cd6997f35a21f6caa0f78e87fc84 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 11 Oct 2015 13:48:05 +0200 Subject: qlcnic: constify qlcnic_mbx_ops structure The only instance of a qlcnic_mbx_ops structure is never modified. Thus the declaration of the structure and all references to the structure type can be made const. In the definition of the qlcnic_mailbox structure, the ops field is no longer lined up with the other fields. This was left as is, to avoid a lot of trivial changes on the other lines. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Acked-by: Sony Chacko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index d6696cf..46bbea8 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -1092,7 +1092,7 @@ struct qlcnic_filter_hash { struct qlcnic_mailbox { struct workqueue_struct *work_q; struct qlcnic_adapter *adapter; - struct qlcnic_mbx_ops *ops; + const struct qlcnic_mbx_ops *ops; struct work_struct work; struct completion completion; struct list_head cmd_q; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c index 9f0bdd9..37a731b 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c @@ -4048,7 +4048,7 @@ static void qlcnic_83xx_mailbox_worker(struct work_struct *work) struct qlcnic_mailbox *mbx = container_of(work, struct qlcnic_mailbox, work); struct qlcnic_adapter *adapter = mbx->adapter; - struct qlcnic_mbx_ops *mbx_ops = mbx->ops; + const struct qlcnic_mbx_ops *mbx_ops = mbx->ops; struct device *dev = &adapter->pdev->dev; atomic_t *rsp_status = &mbx->rsp_status; struct list_head *head = &mbx->cmd_q; @@ -4098,7 +4098,7 @@ static void qlcnic_83xx_mailbox_worker(struct work_struct *work) } } -static struct qlcnic_mbx_ops qlcnic_83xx_mbx_ops = { +static const struct qlcnic_mbx_ops qlcnic_83xx_mbx_ops = { .enqueue_cmd = qlcnic_83xx_enqueue_mbx_cmd, .dequeue_cmd = qlcnic_83xx_dequeue_mbx_cmd, .decode_resp = qlcnic_83xx_decode_mbx_rsp, -- cgit v0.10.2 From 486798001b92eacbf9f809787a6348750c174035 Mon Sep 17 00:00:00 2001 From: Sowmini Varadhan Date: Sun, 11 Oct 2015 16:46:03 -0400 Subject: RDS: Invoke ->laddr_check() in rds_bind() for explicitly bound transports. The IP address passed to rds_bind() should be vetted by the transport's ->laddr_check() for a previously bound transport. This needs to be done to avoid cases where, for example, the application has asked for an IB transport, but the IP address passed to bind is only usable on ethernet interfaces. Signed-off-by: Sowmini Varadhan Acked-by: Santosh Shilimkar Signed-off-by: David S. Miller diff --git a/net/rds/bind.c b/net/rds/bind.c index bc6b93e..6192566 100644 --- a/net/rds/bind.c +++ b/net/rds/bind.c @@ -196,7 +196,14 @@ int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) goto out; if (rs->rs_transport) { /* previously bound */ - ret = 0; + trans = rs->rs_transport; + if (trans->laddr_check(sock_net(sock->sk), + sin->sin_addr.s_addr) != 0) { + ret = -ENOPROTOOPT; + rds_remove_bound(rs); + } else { + ret = 0; + } goto out; } trans = rds_trans_get_preferred(sock_net(sock->sk), -- cgit v0.10.2 From 241b271952eb319622a6cc740a72abe41fc27ad6 Mon Sep 17 00:00:00 2001 From: Sowmini Varadhan Date: Sun, 11 Oct 2015 16:49:44 -0400 Subject: RDS-TCP: Reset tcp callbacks if re-using an outgoing socket in rds_tcp_accept_one() Consider the following "duelling syn" sequence between two peers A and B: A B SYN1 --> <-- SYN2 SYN2ACK --> Note that the SYN/ACK has already been sent out by TCP before rds_tcp_accept_one() gets invoked as part of callbacks. If the inet_addr(A) is numerically less than inet_addr(B), the arbitration scheme in rds_tcp_accept_one() will prefer the TCP connection triggered by SYN1, and will send a CLOSE for the SYN2 (just after the SYN2ACK was sent). Since B also follows the same arbitration scheme, it will send the SYN-ACK for SYN1 that will set up a healthy ESTABLISHED connection on both sides. B will also get a CLOSE for SYN2, which should result in the cleanup of the TCP state machine for SYN2, but it should not trigger any stale RDS-TCP callbacks (such as ->writespace, ->state_change etc), that would disrupt the progress of the SYN2 based RDS-TCP connection. Thus the arbitration scheme in rds_tcp_accept_one() should restore rds_tcp callbacks for the winner before setting them up for the new accept socket, and also make sure that conn->c_outgoing is set to 0 so that we do not trigger any reconnect attempts on the passive side of the tcp socket in the future, in conformance with commit c82ac7e69efe ("net/rds: RDS-TCP: only initiate reconnect attempt on outgoing TCP socket.") Signed-off-by: Sowmini Varadhan Acked-by: Santosh Shilimkar Signed-off-by: David S. Miller diff --git a/net/rds/tcp_listen.c b/net/rds/tcp_listen.c index 1d90240..0936a4a 100644 --- a/net/rds/tcp_listen.c +++ b/net/rds/tcp_listen.c @@ -125,6 +125,9 @@ int rds_tcp_accept_one(struct socket *sock) new_sock = NULL; ret = 0; goto out; + } else if (rs_tcp->t_sock) { + rds_tcp_restore_callbacks(rs_tcp->t_sock, rs_tcp); + conn->c_outgoing = 0; } rds_conn_transition(conn, RDS_CONN_DOWN, RDS_CONN_CONNECTING); -- cgit v0.10.2 From ede8098d0fef46ae48e59fcf7088149ca424d959 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sun, 11 Oct 2015 18:08:35 -0400 Subject: net: dsa: mv88e6xxx: bridges do not need an FID With 88E6352 and similar switch chips, each port has a map to restrict which output port this input port can egress frames to. The current driver code implements hardware bridging using this feature, and assigns to a bridge group the FID of its first member. Now that 802.1Q is fully implemented in this driver, a Linux bridge which is a simple untagged VLAN, already gets its own FID. This patch gets rid of the per-bridge FID and explicits the usage of the port based VLAN map feature. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index 87b405e..8d62bb3 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -1046,11 +1046,6 @@ static int _mv88e6xxx_atu_flush(struct dsa_switch *ds, u16 fid, bool static_too) return _mv88e6xxx_atu_flush_move(ds, &entry, static_too); } -static int _mv88e6xxx_flush_fid(struct dsa_switch *ds, int fid) -{ - return _mv88e6xxx_atu_flush(ds, fid, false); -} - static int _mv88e6xxx_atu_move(struct dsa_switch *ds, u16 fid, int from_port, int to_port, bool static_too) { @@ -1112,130 +1107,56 @@ abort: return ret; } -/* Must be called with smi lock held */ -static int _mv88e6xxx_update_port_config(struct dsa_switch *ds, int port) +static int _mv88e6xxx_port_vlan_map_set(struct dsa_switch *ds, int port, + u16 output_ports) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - u8 fid = ps->fid[port]; - u16 reg = fid << 12; - - if (dsa_is_cpu_port(ds, port)) - reg |= ds->phys_port_mask; - else - reg |= (ps->bridge_mask[fid] | - (1 << dsa_upstream_port(ds))) & ~(1 << port); - - return _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_BASE_VLAN, reg); -} - -/* Must be called with smi lock held */ -static int _mv88e6xxx_update_bridge_config(struct dsa_switch *ds, int fid) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int port; - u32 mask; - int ret; + const u16 mask = (1 << ps->num_ports) - 1; + int reg; - mask = ds->phys_port_mask; - while (mask) { - port = __ffs(mask); - mask &= ~(1 << port); - if (ps->fid[port] != fid) - continue; + reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_BASE_VLAN); + if (reg < 0) + return reg; - ret = _mv88e6xxx_update_port_config(ds, port); - if (ret) - return ret; - } + reg &= ~mask; + reg |= output_ports & mask; - return _mv88e6xxx_flush_fid(ds, fid); + return _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_BASE_VLAN, reg); } /* Bridge handling functions */ -int mv88e6xxx_join_bridge(struct dsa_switch *ds, int port, u32 br_port_mask) +static int mv88e6xxx_map_bridge(struct dsa_switch *ds, u16 members) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int ret = 0; - u32 nmask; - int fid; - - /* If the bridge group is not empty, join that group. - * Otherwise create a new group. - */ - fid = ps->fid[port]; - nmask = br_port_mask & ~(1 << port); - if (nmask) - fid = ps->fid[__ffs(nmask)]; - - nmask = ps->bridge_mask[fid] | (1 << port); - if (nmask != br_port_mask) { - netdev_err(ds->ports[port], - "join: Bridge port mask mismatch fid=%d mask=0x%x expected 0x%x\n", - fid, br_port_mask, nmask); - return -EINVAL; - } + const unsigned long output = members | BIT(dsa_upstream_port(ds)); + int port, err = 0; mutex_lock(&ps->smi_mutex); - ps->bridge_mask[fid] = br_port_mask; + for_each_set_bit(port, &output, ps->num_ports) { + if (dsa_is_cpu_port(ds, port)) + continue; - if (fid != ps->fid[port]) { - clear_bit(ps->fid[port], ps->fid_bitmap); - ps->fid[port] = fid; - ret = _mv88e6xxx_update_bridge_config(ds, fid); + err = _mv88e6xxx_port_vlan_map_set(ds, port, output & ~port); + if (err) + break; } mutex_unlock(&ps->smi_mutex); - return ret; + return err; } -int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - u8 fid, newfid; - int ret; - - fid = ps->fid[port]; - if (ps->bridge_mask[fid] != br_port_mask) { - netdev_err(ds->ports[port], - "leave: Bridge port mask mismatch fid=%d mask=0x%x expected 0x%x\n", - fid, br_port_mask, ps->bridge_mask[fid]); - return -EINVAL; - } - - /* If the port was the last port of a bridge, we are done. - * Otherwise assign a new fid to the port, and fix up - * the bridge configuration. - */ - if (br_port_mask == (1 << port)) - return 0; - - mutex_lock(&ps->smi_mutex); - - newfid = find_next_zero_bit(ps->fid_bitmap, VLAN_N_VID, 1); - if (unlikely(newfid > ps->num_ports)) { - netdev_err(ds->ports[port], "all first %d FIDs are used\n", - ps->num_ports); - ret = -ENOSPC; - goto unlock; - } - - ps->fid[port] = newfid; - set_bit(newfid, ps->fid_bitmap); - ps->bridge_mask[fid] &= ~(1 << port); - ps->bridge_mask[newfid] = 1 << port; - - ret = _mv88e6xxx_update_bridge_config(ds, fid); - if (!ret) - ret = _mv88e6xxx_update_bridge_config(ds, newfid); - -unlock: - mutex_unlock(&ps->smi_mutex); +int mv88e6xxx_join_bridge(struct dsa_switch *ds, int port, u32 br_port_mask) +{ + return mv88e6xxx_map_bridge(ds, br_port_mask); +} - return ret; +int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask) +{ + return mv88e6xxx_map_bridge(ds, br_port_mask & ~port); } int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state) @@ -2231,10 +2152,12 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) ps->fid[port] = fid; set_bit(fid, ps->fid_bitmap); - if (!dsa_is_cpu_port(ds, port)) - ps->bridge_mask[fid] = 1 << port; + if (dsa_is_cpu_port(ds, port)) + reg = BIT(ps->num_ports) - 1; + else + reg = BIT(dsa_upstream_port(ds)); - ret = _mv88e6xxx_update_port_config(ds, port); + ret = _mv88e6xxx_port_vlan_map_set(ds, port, reg & ~port); if (ret) goto abort; diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h index 8325c11..10387c2 100644 --- a/drivers/net/dsa/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx.h @@ -406,7 +406,6 @@ struct mv88e6xxx_priv_state { DECLARE_BITMAP(fid_bitmap, VLAN_N_VID); /* FIDs 1 to 4095 available */ u16 fid[DSA_MAX_PORTS]; /* per (non-bridged) port FID */ - u16 bridge_mask[DSA_MAX_PORTS]; /* br groups (indexed by FID) */ unsigned long port_state_update_mask; u8 port_state[DSA_MAX_PORTS]; -- cgit v0.10.2 From f02bdffca29bc41e440ac67ffd47b56834d3bf85 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sun, 11 Oct 2015 18:08:36 -0400 Subject: net: dsa: mv88e6xxx: do not support per-port FID Since we configure a switch chip through a Linux bridge, and a bridge is implemented as a VLAN, there is no need for per-port FID anymore. This patch gets rid of this and simplifies the driver code since we can now directly map all 4095 FIDs available to all VLANs. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index 8d62bb3..b2b99f8 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -1468,6 +1468,7 @@ static int _mv88e6xxx_vlan_init(struct dsa_switch *ds, u16 vid, struct mv88e6xxx_vtu_stu_entry vlan = { .valid = true, .vid = vid, + .fid = vid, /* We use one FID per VLAN */ }; int i; @@ -1501,22 +1502,10 @@ static int _mv88e6xxx_vlan_init(struct dsa_switch *ds, u16 vid, return err; } - /* Non-bridged ports and bridge groups use FIDs from 1 to - * num_ports; VLANs use FIDs from num_ports+1 to 4095. - */ - vlan.fid = find_next_zero_bit(ps->fid_bitmap, VLAN_N_VID, - ps->num_ports + 1); - if (unlikely(vlan.fid == VLAN_N_VID)) { - pr_err("no more FID available for VLAN %d\n", vid); - return -ENOSPC; - } - /* Clear all MAC addresses from the new database */ err = _mv88e6xxx_atu_flush(ds, vlan.fid, true); if (err) return err; - - set_bit(vlan.fid, ps->fid_bitmap); } *entry = vlan; @@ -1556,7 +1545,6 @@ int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_vtu_stu_entry vlan; - bool keep = false; int i, err; mutex_lock(&ps->smi_mutex); @@ -1574,28 +1562,22 @@ int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid) vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER; /* keep the VLAN unless all ports are excluded */ + vlan.valid = false; for (i = 0; i < ps->num_ports; ++i) { if (dsa_is_cpu_port(ds, i)) continue; if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) { - keep = true; + vlan.valid = true; break; } } - vlan.valid = keep; err = _mv88e6xxx_vtu_loadpurge(ds, &vlan); if (err) goto unlock; err = _mv88e6xxx_atu_remove(ds, vlan.fid, port, false); - if (err) - goto unlock; - - if (!keep) - clear_bit(vlan.fid, ps->fid_bitmap); - unlock: mutex_unlock(&ps->smi_mutex); @@ -1722,37 +1704,13 @@ static int _mv88e6xxx_atu_load(struct dsa_switch *ds, return _mv88e6xxx_atu_cmd(ds, GLOBAL_ATU_OP_LOAD_DB); } -static int _mv88e6xxx_port_vid_to_fid(struct dsa_switch *ds, int port, u16 vid) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - struct mv88e6xxx_vtu_stu_entry vlan; - int err; - - if (vid == 0) - return ps->fid[port]; - - err = _mv88e6xxx_port_vtu_getnext(ds, port, vid - 1, &vlan); - if (err) - return err; - - if (vlan.vid == vid) - return vlan.fid; - - return -ENOENT; -} - static int _mv88e6xxx_port_fdb_load(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid, u8 state) { struct mv88e6xxx_atu_entry entry = { 0 }; - int ret; - - ret = _mv88e6xxx_port_vid_to_fid(ds, port, vid); - if (ret < 0) - return ret; - entry.fid = ret; + entry.fid = vid; /* We use one FID per VLAN */ entry.state = state; ether_addr_copy(entry.mac, addr); if (state != GLOBAL_ATU_DATA_STATE_UNUSED) { @@ -1767,6 +1725,10 @@ int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port, const struct switchdev_obj_port_fdb *fdb, struct switchdev_trans *trans) { + /* We don't use per-port FDB */ + if (fdb->vid == 0) + return -EOPNOTSUPP; + /* We don't need any dynamic resource from the kernel (yet), * so skip the prepare phase. */ @@ -1864,16 +1826,11 @@ int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port, { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_atu_entry next; - u16 fid; + u16 fid = *vid; /* We use one FID per VLAN */ int ret; mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_port_vid_to_fid(ds, port, *vid); - if (ret < 0) - goto unlock; - fid = ret; - do { if (is_broadcast_ether_addr(addr)) { struct mv88e6xxx_vtu_stu_entry vtu; @@ -1924,7 +1881,7 @@ static void mv88e6xxx_bridge_work(struct work_struct *work) static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int ret, fid; + int ret; u16 reg; mutex_lock(&ps->smi_mutex); @@ -2143,15 +2100,11 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) if (ret) goto abort; - /* Port based VLAN map: give each port its own address + /* Port based VLAN map: do not give each port its own address * database, allow the CPU port to talk to each of the 'real' * ports, and allow each of the 'real' ports to only talk to * the upstream port. */ - fid = port + 1; - ps->fid[port] = fid; - set_bit(fid, ps->fid_bitmap); - if (dsa_is_cpu_port(ds, port)) reg = BIT(ps->num_ports) - 1; else diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h index 10387c2..9b6104b 100644 --- a/drivers/net/dsa/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx.h @@ -402,11 +402,6 @@ struct mv88e6xxx_priv_state { int id; /* switch product id */ int num_ports; /* number of switch ports */ - /* hw bridging */ - - DECLARE_BITMAP(fid_bitmap, VLAN_N_VID); /* FIDs 1 to 4095 available */ - u16 fid[DSA_MAX_PORTS]; /* per (non-bridged) port FID */ - unsigned long port_state_update_mask; u8 port_state[DSA_MAX_PORTS]; -- cgit v0.10.2 From efd29b3d8266761570fd3f440e2d5aa24c678725 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sun, 11 Oct 2015 18:08:37 -0400 Subject: net: dsa: do not warn unsupported bridge ops A DSA driver may not provide the port_join_bridge and port_leave_bridge functions, so don't warn in such case. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller diff --git a/net/dsa/slave.c b/net/dsa/slave.c index bb2bd3b..43d7342 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -1276,7 +1276,7 @@ int dsa_slave_netdevice_event(struct notifier_block *unused, goto out; err = dsa_slave_master_changed(dev); - if (err) + if (err && err != -EOPNOTSUPP) netdev_warn(dev, "failed to reflect master change\n"); break; -- cgit v0.10.2 From 5fe7f68016ff9dcb59632071f9abf30296bbad3c Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sun, 11 Oct 2015 18:08:38 -0400 Subject: net: dsa: mv88e6xxx: fix hardware bridging Playing with the VLAN map of every port to implement "hardware bridging" in the 88E6352 driver was a hack until full 802.1Q was supported. Indeed with 802.1Q port mode "Disabled" or "Fallback", this feature is used to restrict which output ports an input port can egress frames to. A Linux bridge is an untagged VLAN. With full 802.1Q support, we don't need this hack anymore and can use the "Secure" strict 802.1Q port mode. With this mode, the port-based VLAN map still needs to be configured, but all the logic is VTU-centric. This means that the switch only cares about rules described in its hardware VLAN table, which is exactly what Linux bridge expects and what we want. Note also that the hardware bridging was broken with the previous flexible "Fallback" 802.1Q port mode. Here's an example: Port0 and Port1 belong to the same bridge. If Port0 sends crafted tagged frames with VID 200 to Port1, Port1 receives it. Even if Port1 is in hardware VLAN 200, but not Port0, Port1 will still receive it, because Fallback mode doesn't care about invalid VID or non-member source port. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller diff --git a/drivers/net/dsa/mv88e6171.c b/drivers/net/dsa/mv88e6171.c index ca3330a..dfca352 100644 --- a/drivers/net/dsa/mv88e6171.c +++ b/drivers/net/dsa/mv88e6171.c @@ -113,8 +113,6 @@ struct dsa_switch_driver mv88e6171_switch_driver = { #endif .get_regs_len = mv88e6xxx_get_regs_len, .get_regs = mv88e6xxx_get_regs, - .port_join_bridge = mv88e6xxx_join_bridge, - .port_leave_bridge = mv88e6xxx_leave_bridge, .port_stp_update = mv88e6xxx_port_stp_update, .port_pvid_get = mv88e6xxx_port_pvid_get, .port_pvid_set = mv88e6xxx_port_pvid_set, diff --git a/drivers/net/dsa/mv88e6352.c b/drivers/net/dsa/mv88e6352.c index 078a358..796fdcb 100644 --- a/drivers/net/dsa/mv88e6352.c +++ b/drivers/net/dsa/mv88e6352.c @@ -340,8 +340,6 @@ struct dsa_switch_driver mv88e6352_switch_driver = { .set_eeprom = mv88e6352_set_eeprom, .get_regs_len = mv88e6xxx_get_regs_len, .get_regs = mv88e6xxx_get_regs, - .port_join_bridge = mv88e6xxx_join_bridge, - .port_leave_bridge = mv88e6xxx_leave_bridge, .port_stp_update = mv88e6xxx_port_stp_update, .port_pvid_get = mv88e6xxx_port_pvid_get, .port_pvid_set = mv88e6xxx_port_pvid_set, diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index b2b99f8..4591240 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -1124,41 +1124,6 @@ static int _mv88e6xxx_port_vlan_map_set(struct dsa_switch *ds, int port, return _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_BASE_VLAN, reg); } -/* Bridge handling functions */ - -static int mv88e6xxx_map_bridge(struct dsa_switch *ds, u16 members) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - const unsigned long output = members | BIT(dsa_upstream_port(ds)); - int port, err = 0; - - mutex_lock(&ps->smi_mutex); - - for_each_set_bit(port, &output, ps->num_ports) { - if (dsa_is_cpu_port(ds, port)) - continue; - - err = _mv88e6xxx_port_vlan_map_set(ds, port, output & ~port); - if (err) - break; - } - - mutex_unlock(&ps->smi_mutex); - - return err; -} - - -int mv88e6xxx_join_bridge(struct dsa_switch *ds, int port, u32 br_port_mask) -{ - return mv88e6xxx_map_bridge(ds, br_port_mask); -} - -int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask) -{ - return mv88e6xxx_map_bridge(ds, br_port_mask & ~port); -} - int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); @@ -2007,7 +1972,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) reg |= PORT_CONTROL_2_FORWARD_UNKNOWN; } - reg |= PORT_CONTROL_2_8021Q_FALLBACK; + reg |= PORT_CONTROL_2_8021Q_SECURE; if (reg) { ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), @@ -2101,15 +2066,9 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) goto abort; /* Port based VLAN map: do not give each port its own address - * database, allow the CPU port to talk to each of the 'real' - * ports, and allow each of the 'real' ports to only talk to - * the upstream port. + * database, and allow every port to egress frames on all other ports. */ - if (dsa_is_cpu_port(ds, port)) - reg = BIT(ps->num_ports) - 1; - else - reg = BIT(dsa_upstream_port(ds)); - + reg = BIT(ps->num_ports) - 1; /* all ports */ ret = _mv88e6xxx_port_vlan_map_set(ds, port, reg & ~port); if (ret) goto abort; diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h index 9b6104b..4ba69f3 100644 --- a/drivers/net/dsa/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx.h @@ -458,8 +458,6 @@ int mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr, int regnum, int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e); int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, struct phy_device *phydev, struct ethtool_eee *e); -int mv88e6xxx_join_bridge(struct dsa_switch *ds, int port, u32 br_port_mask); -int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask); int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state); int mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *vid); int mv88e6xxx_port_pvid_set(struct dsa_switch *ds, int port, u16 vid); -- cgit v0.10.2 From cc02aa8e41c50f690d0bb22ed5629468483421b7 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Mon, 12 Oct 2015 14:01:39 +0200 Subject: switchdev: enforce no pvid flag in vlan ranges We shouldn't allow BRIDGE_VLAN_INFO_PVID flag in VLAN ranges. Signed-off-by: Nikolay Aleksandrov Acked-by: Elad Raz Acked-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index 7a9ab90..b8aaf820 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -727,6 +727,9 @@ static int switchdev_port_br_afspec(struct net_device *dev, if (vlan.vid_begin) return -EINVAL; vlan.vid_begin = vinfo->vid; + /* don't allow range of pvids */ + if (vlan.flags & BRIDGE_VLAN_INFO_PVID) + return -EINVAL; } else if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END) { if (!vlan.vid_begin) return -EINVAL; -- cgit v0.10.2 From af3793921d49a772ec1079449219bad4baa0bc96 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Mon, 12 Oct 2015 17:55:55 +0200 Subject: bridge: fix gc_timer mod/del race condition commit c62987bbd8a1 ("bridge: push bridge setting ageing_time down to switchdev") introduced a timer race condition because the gc_timer can get rearmed after it's supposedly stopped and flushed in br_dev_delete() leading to a use of freed memory. So take rtnl to sync with bridge destruction when setting ageing_timer. Here's the trace reproduced with these two commands running in parallel: while :; do echo 10000 > /sys/class/net/br0/bridge/ageing_timer; done; while :; do brctl addbr br0; ip l set br0 up; ip l set br0 down; brctl delbr br0; done; [ 300.000029] BUG: unable to handle kernel paging request at ffffffff811c59d3 [ 300.000263] IP: [] __internal_add_timer+0x2e/0xd0 [ 300.000422] PGD 1a0f067 PUD 1a10063 PMD 10001e1 [ 300.000639] Oops: 0003 [#1] SMP [ 300.000793] Modules linked in: bridge stp llc nfsd auth_rpcgss oid_registry nfs_acl nfs lockd grace fscache sunrpc crct10dif_pclmul crc32_pclmul crc32c_intel ghash_clmulni_intel ppdev aesni_intel aes_x86_64 glue_helper lrw gf128mul ablk_helper cryptd snd_hda_codec_generic qxl drm_kms_helper psmouse pcspkr ttm snd_hda_intel 9pnet_virtio evdev serio_raw joydev snd_hda_codec 9pnet virtio_balloon drm snd_hwdep virtio_console snd_hda_core pvpanic snd_pcm i2c_piix4 snd_timer acpi_cpufreq parport_pc snd parport soundcore button processor i2c_core ipv6 autofs4 hid_generic usbhid hid ext4 crc16 mbcache jbd2 sg sr_mod cdrom ata_generic virtio_blk virtio_net e1000 ehci_pci uhci_hcd ehci_hcd usbcore usb_common floppy ata_piix libata virtio_pci virtio_ring virtio scsi_mod [ 300.004008] CPU: 1 PID: 1169 Comm: bash Not tainted 4.3.0-rc3+ #46 [ 300.004008] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 [ 300.004008] task: ffff880035be2200 ti: ffff88003795c000 task.ti: ffff88003795c000 [ 300.004008] RIP: 0010:[] [] __internal_add_timer+0x2e/0xd0 [ 300.004008] RSP: 0018:ffff88003fd03e78 EFLAGS: 00010046 [ 300.004008] RAX: ffff88003fd0ef60 RBX: 840fc78949c08548 RCX: 00000001ffffffff [ 300.004008] RDX: 0000000000000000 RSI: ffffffff811c59d3 RDI: ffff88003fd0df00 [ 300.004008] RBP: ffff88003fd03e78 R08: 00000000ffffffff R09: 0000000000000000 [ 300.004008] R10: 0000000000000000 R11: 0000000000000000 R12: ffff88003fd0df00 [ 300.004008] R13: 0000000000000000 R14: 0000000000000001 R15: ffffffff816032e0 [ 300.004008] FS: 00007fcbdd609700(0000) GS:ffff88003fd00000(0000) knlGS:0000000000000000 [ 300.004008] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 300.004008] CR2: ffffffff811c59d3 CR3: 0000000037879000 CR4: 00000000000406e0 [ 300.004008] Stack: [ 300.004008] ffff88003fd03ea8 ffffffff810f1775 ffff88003c8cb958 ffff88003fd0df00 [ 300.004008] 0000000000000000 0000000000000001 ffff88003fd03f18 ffffffff810f28c4 [ 300.004008] ffff88003fd0eb68 ffff88003fd0e968 ffff88003fd0e768 ffff88003fd0df68 [ 300.004008] Call Trace: [ 300.004008] [ 300.004008] [] cascade+0x45/0x70 [ 300.004008] [] run_timer_softirq+0x2f4/0x340 [ 300.004008] [] __do_softirq+0xd0/0x440 [ 300.004008] [] irq_exit+0xb3/0xc0 [ 300.004008] [] smp_apic_timer_interrupt+0x42/0x50 [ 300.004008] [] apic_timer_interrupt+0x87/0x90 [ 300.004008] [ 300.004008] [] ? create_object+0x13c/0x2e0 [ 300.004008] [] ? __kernel_text_address+0x4e/0x70 [ 300.004008] [] ? __kernel_text_address+0x4e/0x70 [ 300.004008] [] print_context_stack+0x7f/0xf0 [ 300.004008] [] dump_trace+0x11b/0x300 [ 300.004008] [] save_stack_trace+0x2b/0x50 [ 300.004008] [] create_object+0x13c/0x2e0 [ 300.004008] [] kmemleak_alloc+0x4e/0xb0 [ 300.004008] [] kmem_cache_alloc_trace+0x18d/0x2f0 [ 300.004008] [] kernfs_fop_open+0xc9/0x380 [ 300.004008] [] do_dentry_open+0x1ff/0x2f0 [ 300.004008] [] ? kernfs_fop_release+0x70/0x70 [ 300.004008] [] vfs_open+0x59/0x60 [ 300.004008] [] path_openat+0x1ce/0x1260 [ 300.004008] [] do_filp_open+0x7e/0xe0 [ 300.004008] [] ? __alloc_fd+0xaf/0x180 [ 300.004008] [] do_sys_open+0x12b/0x210 [ 300.004008] [] SyS_open+0x1e/0x20 [ 300.004008] [] entry_SYSCALL_64_fastpath+0x16/0x7a [ 300.004008] Code: 66 90 48 8b 46 10 48 8b 4f 40 55 48 89 c2 48 89 e5 48 29 ca 48 81 fa ff 00 00 00 77 20 0f b6 c0 48 8d 44 c7 68 48 8b 10 48 85 d2 <48> 89 16 74 04 48 89 72 08 48 89 30 48 89 46 08 5d c3 48 81 fa [ 300.004008] RIP [] __internal_add_timer+0x2e/0xd0 [ 300.004008] RSP [ 300.004008] CR2: ffffffff811c59d3 Fixes: c62987bbd8a1 ("bridge: push bridge setting ageing_time down to switchdev") Signed-off-by: Nikolay Aleksandrov Reviewed-by: Jiri Pirko Acked-by: Scott Feldman Signed-off-by: David S. Miller diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c index 04ef192..8365bd5 100644 --- a/net/bridge/br_sysfs_br.c +++ b/net/bridge/br_sysfs_br.c @@ -102,7 +102,15 @@ static ssize_t ageing_time_show(struct device *d, static int set_ageing_time(struct net_bridge *br, unsigned long val) { - return br_set_ageing_time(br, val); + int ret; + + if (!rtnl_trylock()) + return restart_syscall(); + + ret = br_set_ageing_time(br, val); + rtnl_unlock(); + + return ret; } static ssize_t ageing_time_store(struct device *d, -- cgit v0.10.2 From ccf3c8c3fe1bd4828556650ae7928da6ffb4aaf6 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 12 Oct 2015 11:47:07 -0700 Subject: net: Add IPv6 support to l3mdev Add operations to retrieve cached IPv6 dst entry from l3mdev device and lookup IPv6 source address. Signed-off-by: David Ahern Signed-off-by: David S. Miller diff --git a/include/net/l3mdev.h b/include/net/l3mdev.h index 44a19a1..774d85b 100644 --- a/include/net/l3mdev.h +++ b/include/net/l3mdev.h @@ -19,14 +19,22 @@ * @l3mdev_get_rtable: Get cached IPv4 rtable (dst_entry) for device * * @l3mdev_get_saddr: Get source address for a flow + * + * @l3mdev_get_rt6_dst: Get cached IPv6 rt6_info (dst_entry) for device */ struct l3mdev_ops { u32 (*l3mdev_fib_table)(const struct net_device *dev); + + /* IPv4 ops */ struct rtable * (*l3mdev_get_rtable)(const struct net_device *dev, const struct flowi4 *fl4); void (*l3mdev_get_saddr)(struct net_device *dev, struct flowi4 *fl4); + + /* IPv6 ops */ + struct dst_entry * (*l3mdev_get_rt6_dst)(const struct net_device *dev, + const struct flowi6 *fl6); }; #ifdef CONFIG_NET_L3_MASTER_DEV @@ -123,6 +131,31 @@ static inline void l3mdev_get_saddr(struct net *net, int ifindex, } } +static inline struct dst_entry *l3mdev_get_rt6_dst(const struct net_device *dev, + const struct flowi6 *fl6) +{ + if (netif_is_l3_master(dev) && dev->l3mdev_ops->l3mdev_get_rt6_dst) + return dev->l3mdev_ops->l3mdev_get_rt6_dst(dev, fl6); + + return NULL; +} + +static inline +struct dst_entry *l3mdev_rt6_dst_by_oif(struct net *net, + const struct flowi6 *fl6) +{ + struct dst_entry *dst = NULL; + struct net_device *dev; + + dev = dev_get_by_index(net, fl6->flowi6_oif); + if (dev) { + dst = l3mdev_get_rt6_dst(dev, fl6); + dev_put(dev); + } + + return dst; +} + #else static inline int l3mdev_master_ifindex_rcu(struct net_device *dev) @@ -171,6 +204,19 @@ static inline void l3mdev_get_saddr(struct net *net, int ifindex, struct flowi4 *fl4) { } + +static inline +struct dst_entry *l3mdev_get_rt6_dst(const struct net_device *dev, + const struct flowi6 *fl6) +{ + return NULL; +} +static inline +struct dst_entry *l3mdev_rt6_dst_by_oif(struct net *net, + const struct flowi6 *fl6) +{ + return NULL; +} #endif #endif /* _NET_L3MDEV_H_ */ -- cgit v0.10.2 From c4850687783717fa854554965c4bc85625d0e4a8 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 12 Oct 2015 11:47:08 -0700 Subject: net: Export fib6_get_table and nd_tbl Signed-off-by: David Ahern Signed-off-by: David S. Miller diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 7d2e002..09fddf7 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -264,6 +264,7 @@ struct fib6_table *fib6_get_table(struct net *net, u32 id) return NULL; } +EXPORT_SYMBOL_GPL(fib6_get_table); static void __net_init fib6_tables_init(struct net *net) { diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index b18012f..9f8a824 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -147,6 +147,7 @@ struct neigh_table nd_tbl = { .gc_thresh2 = 512, .gc_thresh3 = 1024, }; +EXPORT_SYMBOL_GPL(nd_tbl); static void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data) { -- cgit v0.10.2 From 35402e31366349a32b505afdfe856aeeb8d939a0 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 12 Oct 2015 11:47:09 -0700 Subject: net: Add IPv6 support to VRF device Add support for IPv6 to VRF device driver. Implemenation parallels what has been done for IPv4. Signed-off-by: David Ahern Signed-off-by: David S. Miller diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index b9ebd0d..f184fb5 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -298,8 +298,10 @@ config NLMON config NET_VRF tristate "Virtual Routing and Forwarding (Lite)" - depends on IP_MULTIPLE_TABLES && IPV6_MULTIPLE_TABLES + depends on IP_MULTIPLE_TABLES depends on NET_L3_MASTER_DEV + depends on IPV6 || IPV6=n + depends on IPV6_MULTIPLE_TABLES || IPV6=n ---help--- This option enables the support for mapping interfaces into VRF's. The support enables VRF devices. diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 191579a..92fa3e1 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,7 @@ struct slave_queue { struct net_vrf { struct slave_queue queue; struct rtable *rth; + struct rt6_info *rt6; u32 tb_id; }; @@ -104,12 +106,56 @@ static struct dst_ops vrf_dst_ops = { .default_advmss = vrf_default_advmss, }; +/* neighbor handling is done with actual device; do not want + * to flip skb->dev for those ndisc packets. This really fails + * for multiple next protocols (e.g., NEXTHDR_HOP). But it is + * a start. + */ +#if IS_ENABLED(CONFIG_IPV6) +static bool check_ipv6_frame(const struct sk_buff *skb) +{ + const struct ipv6hdr *ipv6h = (struct ipv6hdr *)skb->data; + size_t hlen = sizeof(*ipv6h); + bool rc = true; + + if (skb->len < hlen) + goto out; + + if (ipv6h->nexthdr == NEXTHDR_ICMP) { + const struct icmp6hdr *icmph; + + if (skb->len < hlen + sizeof(*icmph)) + goto out; + + icmph = (struct icmp6hdr *)(skb->data + sizeof(*ipv6h)); + switch (icmph->icmp6_type) { + case NDISC_ROUTER_SOLICITATION: + case NDISC_ROUTER_ADVERTISEMENT: + case NDISC_NEIGHBOUR_SOLICITATION: + case NDISC_NEIGHBOUR_ADVERTISEMENT: + case NDISC_REDIRECT: + rc = false; + break; + } + } + +out: + return rc; +} +#else +static bool check_ipv6_frame(const struct sk_buff *skb) +{ + return false; +} +#endif + static bool is_ip_rx_frame(struct sk_buff *skb) { switch (skb->protocol) { case htons(ETH_P_IP): - case htons(ETH_P_IPV6): return true; + case htons(ETH_P_IPV6): + return check_ipv6_frame(skb); } return false; } @@ -169,12 +215,53 @@ static struct rtnl_link_stats64 *vrf_get_stats64(struct net_device *dev, return stats; } +#if IS_ENABLED(CONFIG_IPV6) +static netdev_tx_t vrf_process_v6_outbound(struct sk_buff *skb, + struct net_device *dev) +{ + const struct ipv6hdr *iph = ipv6_hdr(skb); + struct net *net = dev_net(skb->dev); + struct flowi6 fl6 = { + /* needed to match OIF rule */ + .flowi6_oif = dev->ifindex, + .flowi6_iif = LOOPBACK_IFINDEX, + .daddr = iph->daddr, + .saddr = iph->saddr, + .flowlabel = ip6_flowinfo(iph), + .flowi6_mark = skb->mark, + .flowi6_proto = iph->nexthdr, + .flowi6_flags = FLOWI_FLAG_L3MDEV_SRC | FLOWI_FLAG_SKIP_NH_OIF, + }; + int ret = NET_XMIT_DROP; + struct dst_entry *dst; + struct dst_entry *dst_null = &net->ipv6.ip6_null_entry->dst; + + dst = ip6_route_output(net, NULL, &fl6); + if (dst == dst_null) + goto err; + + skb_dst_drop(skb); + skb_dst_set(skb, dst); + + ret = ip6_local_out(net, skb->sk, skb); + if (unlikely(net_xmit_eval(ret))) + dev->stats.tx_errors++; + else + ret = NET_XMIT_SUCCESS; + + return ret; +err: + vrf_tx_error(dev, skb); + return NET_XMIT_DROP; +} +#else static netdev_tx_t vrf_process_v6_outbound(struct sk_buff *skb, struct net_device *dev) { vrf_tx_error(dev, skb); return NET_XMIT_DROP; } +#endif static int vrf_send_v4_prep(struct sk_buff *skb, struct flowi4 *fl4, struct net_device *vrf_dev) @@ -269,6 +356,157 @@ static netdev_tx_t vrf_xmit(struct sk_buff *skb, struct net_device *dev) return ret; } +#if IS_ENABLED(CONFIG_IPV6) +static struct dst_entry *vrf_ip6_check(struct dst_entry *dst, u32 cookie) +{ + return dst; +} + +static struct dst_ops vrf_dst_ops6 = { + .family = AF_INET6, + .local_out = ip6_local_out, + .check = vrf_ip6_check, + .mtu = vrf_v4_mtu, + .destroy = vrf_dst_destroy, + .default_advmss = vrf_default_advmss, +}; + +static int init_dst_ops6_kmem_cachep(void) +{ + vrf_dst_ops6.kmem_cachep = kmem_cache_create("vrf_ip6_dst_cache", + sizeof(struct rt6_info), + 0, + SLAB_HWCACHE_ALIGN, + NULL); + + if (!vrf_dst_ops6.kmem_cachep) + return -ENOMEM; + + return 0; +} + +static void free_dst_ops6_kmem_cachep(void) +{ + kmem_cache_destroy(vrf_dst_ops6.kmem_cachep); +} + +static int vrf_input6(struct sk_buff *skb) +{ + skb->dev->stats.rx_errors++; + kfree_skb(skb); + return 0; +} + +/* modelled after ip6_finish_output2 */ +static int vrf_finish_output6(struct net *net, struct sock *sk, + struct sk_buff *skb) +{ + struct dst_entry *dst = skb_dst(skb); + struct net_device *dev = dst->dev; + struct neighbour *neigh; + struct in6_addr *nexthop; + int ret; + + skb->protocol = htons(ETH_P_IPV6); + skb->dev = dev; + + rcu_read_lock_bh(); + nexthop = rt6_nexthop((struct rt6_info *)dst, &ipv6_hdr(skb)->daddr); + neigh = __ipv6_neigh_lookup_noref(dst->dev, nexthop); + if (unlikely(!neigh)) + neigh = __neigh_create(&nd_tbl, nexthop, dst->dev, false); + if (!IS_ERR(neigh)) { + ret = dst_neigh_output(dst, neigh, skb); + rcu_read_unlock_bh(); + return ret; + } + rcu_read_unlock_bh(); + + IP6_INC_STATS(dev_net(dst->dev), + ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES); + kfree_skb(skb); + return -EINVAL; +} + +/* modelled after ip6_output */ +static int vrf_output6(struct net *net, struct sock *sk, struct sk_buff *skb) +{ + return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, + net, sk, skb, NULL, skb_dst(skb)->dev, + vrf_finish_output6, + !(IP6CB(skb)->flags & IP6SKB_REROUTED)); +} + +static void vrf_rt6_destroy(struct net_vrf *vrf) +{ + dst_destroy(&vrf->rt6->dst); + free_percpu(vrf->rt6->rt6i_pcpu); + vrf->rt6 = NULL; +} + +static int vrf_rt6_create(struct net_device *dev) +{ + struct net_vrf *vrf = netdev_priv(dev); + struct dst_entry *dst; + struct rt6_info *rt6; + int cpu; + int rc = -ENOMEM; + + rt6 = dst_alloc(&vrf_dst_ops6, dev, 0, + DST_OBSOLETE_NONE, + (DST_HOST | DST_NOPOLICY | DST_NOXFRM)); + if (!rt6) + goto out; + + dst = &rt6->dst; + + rt6->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, GFP_KERNEL); + if (!rt6->rt6i_pcpu) { + dst_destroy(dst); + goto out; + } + for_each_possible_cpu(cpu) { + struct rt6_info **p = per_cpu_ptr(rt6->rt6i_pcpu, cpu); + *p = NULL; + } + + memset(dst + 1, 0, sizeof(*rt6) - sizeof(*dst)); + + INIT_LIST_HEAD(&rt6->rt6i_siblings); + INIT_LIST_HEAD(&rt6->rt6i_uncached); + + rt6->dst.input = vrf_input6; + rt6->dst.output = vrf_output6; + + rt6->rt6i_table = fib6_get_table(dev_net(dev), vrf->tb_id); + + atomic_set(&rt6->dst.__refcnt, 2); + + vrf->rt6 = rt6; + rc = 0; +out: + return rc; +} +#else +static int init_dst_ops6_kmem_cachep(void) +{ + return 0; +} + +static void free_dst_ops6_kmem_cachep(void) +{ +} + +static void vrf_rt6_destroy(struct net_vrf *vrf) +{ +} + +static int vrf_rt6_create(struct net_device *dev) +{ + return 0; +} +#endif + /* modelled after ip_finish_output2 */ static int vrf_finish_output(struct net *net, struct sock *sk, struct sk_buff *skb) { @@ -490,6 +728,7 @@ static void vrf_dev_uninit(struct net_device *dev) struct slave *slave, *next; vrf_rtable_destroy(vrf); + vrf_rt6_destroy(vrf); list_for_each_entry_safe(slave, next, head, list) vrf_del_slave(dev, slave->dev); @@ -513,10 +752,15 @@ static int vrf_dev_init(struct net_device *dev) if (!vrf->rth) goto out_stats; + if (vrf_rt6_create(dev) != 0) + goto out_rth; + dev->flags = IFF_MASTER | IFF_NOARP; return 0; +out_rth: + vrf_rtable_destroy(vrf); out_stats: free_percpu(dev->dstats); dev->dstats = NULL; @@ -586,10 +830,30 @@ static void vrf_get_saddr(struct net_device *dev, struct flowi4 *fl4) fl4->flowi4_scope = scope; } +#if IS_ENABLED(CONFIG_IPV6) +static struct dst_entry *vrf_get_rt6_dst(const struct net_device *dev, + const struct flowi6 *fl6) +{ + struct rt6_info *rt = NULL; + + if (!(fl6->flowi6_flags & FLOWI_FLAG_L3MDEV_SRC)) { + struct net_vrf *vrf = netdev_priv(dev); + + rt = vrf->rt6; + atomic_inc(&rt->dst.__refcnt); + } + + return (struct dst_entry *)rt; +} +#endif + static const struct l3mdev_ops vrf_l3mdev_ops = { .l3mdev_fib_table = vrf_fib_table, .l3mdev_get_rtable = vrf_get_rtable, .l3mdev_get_saddr = vrf_get_saddr, +#if IS_ENABLED(CONFIG_IPV6) + .l3mdev_get_rt6_dst = vrf_get_rt6_dst, +#endif }; static void vrf_get_drvinfo(struct net_device *dev, @@ -731,6 +995,10 @@ static int __init vrf_init_module(void) if (!vrf_dst_ops.kmem_cachep) return -ENOMEM; + rc = init_dst_ops6_kmem_cachep(); + if (rc != 0) + goto error2; + register_netdevice_notifier(&vrf_notifier_block); rc = rtnl_link_register(&vrf_link_ops); @@ -741,6 +1009,8 @@ static int __init vrf_init_module(void) error: unregister_netdevice_notifier(&vrf_notifier_block); + free_dst_ops6_kmem_cachep(); +error2: kmem_cache_destroy(vrf_dst_ops.kmem_cachep); return rc; } @@ -750,6 +1020,7 @@ static void __exit vrf_cleanup_module(void) rtnl_link_unregister(&vrf_link_ops); unregister_netdevice_notifier(&vrf_notifier_block); kmem_cache_destroy(vrf_dst_ops.kmem_cachep); + free_dst_ops6_kmem_cachep(); } module_init(vrf_init_module); -- cgit v0.10.2 From ca254490c8dfdaddb5df8a763774db0f4c5200c3 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 12 Oct 2015 11:47:10 -0700 Subject: net: Add VRF support to IPv6 stack As with IPv4 support for VRFs added to IPv6 stack by replacing hardcoded table ids with possibly device specific ones and manipulating the oif in the flowi6. The flow flags are used to skip oif compare in nexthop lookups if the device is enslaved to a VRF via the L3 master device. Signed-off-by: David Ahern Signed-off-by: David S. Miller diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index c8380f1..f0326aa 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -81,6 +81,7 @@ #include #include #include +#include #include #include #include @@ -2146,7 +2147,7 @@ addrconf_prefix_route(struct in6_addr *pfx, int plen, struct net_device *dev, unsigned long expires, u32 flags) { struct fib6_config cfg = { - .fc_table = RT6_TABLE_PREFIX, + .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_PREFIX, .fc_metric = IP6_RT_PRIO_ADDRCONF, .fc_ifindex = dev->ifindex, .fc_expires = expires, @@ -2179,8 +2180,9 @@ static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx, struct fib6_node *fn; struct rt6_info *rt = NULL; struct fib6_table *table; + u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_PREFIX; - table = fib6_get_table(dev_net(dev), RT6_TABLE_PREFIX); + table = fib6_get_table(dev_net(dev), tb_id); if (!table) return NULL; @@ -2211,7 +2213,7 @@ out: static void addrconf_add_mroute(struct net_device *dev) { struct fib6_config cfg = { - .fc_table = RT6_TABLE_LOCAL, + .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_LOCAL, .fc_metric = IP6_RT_PRIO_ADDRCONF, .fc_ifindex = dev->ifindex, .fc_dst_len = 8, @@ -3029,6 +3031,10 @@ static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route) { struct in6_addr addr; + /* no link local addresses on L3 master devices */ + if (netif_is_l3_master(idev->dev)) + return; + ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0); if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) { diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 6c2b213..efb1c00 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -68,6 +68,7 @@ #include #include #include +#include #include @@ -496,6 +497,9 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) else if (!fl6.flowi6_oif) fl6.flowi6_oif = np->ucast_oif; + if (!fl6.flowi6_oif) + fl6.flowi6_oif = l3mdev_master_ifindex(skb->dev); + dst = icmpv6_route_lookup(net, skb, sk, &fl6); if (IS_ERR(dst)) goto out; @@ -575,7 +579,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) fl6.daddr = ipv6_hdr(skb)->saddr; if (saddr) fl6.saddr = *saddr; - fl6.flowi6_oif = skb->dev->ifindex; + fl6.flowi6_oif = l3mdev_fib_oif(skb->dev); fl6.fl6_icmp_type = ICMPV6_ECHO_REPLY; fl6.flowi6_mark = mark; security_skb_classify_flow(skb, flowi6_to_flowi(&fl6)); diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 32583b5..23f97c4 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -55,6 +55,7 @@ #include #include #include +#include static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *skb) { @@ -885,7 +886,8 @@ static struct dst_entry *ip6_sk_dst_check(struct sock *sk, #ifdef CONFIG_IPV6_SUBTREES ip6_rt_check(&rt->rt6i_src, &fl6->saddr, np->saddr_cache) || #endif - (fl6->flowi6_oif && fl6->flowi6_oif != dst->dev->ifindex)) { + (!(fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF) && + (fl6->flowi6_oif && fl6->flowi6_oif != dst->dev->ifindex))) { dst_release(dst); dst = NULL; } @@ -1037,7 +1039,7 @@ struct dst_entry *ip6_dst_lookup_flow(const struct sock *sk, struct flowi6 *fl6, if (final_dst) fl6->daddr = *final_dst; if (!fl6->flowi6_oif) - fl6->flowi6_oif = dst->dev->ifindex; + fl6->flowi6_oif = l3mdev_fib_oif(dst->dev); return xfrm_lookup_route(sock_net(sk), dst, flowi6_to_flowi(fl6), sk, 0); } diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 9f8a824..3e0f855 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -67,6 +67,7 @@ #include #include #include +#include #include #include @@ -442,8 +443,11 @@ static void ndisc_send_skb(struct sk_buff *skb, if (!dst) { struct flowi6 fl6; + int oif = l3mdev_fib_oif(skb->dev); - icmpv6_flow_init(sk, &fl6, type, saddr, daddr, skb->dev->ifindex); + icmpv6_flow_init(sk, &fl6, type, saddr, daddr, oif); + if (oif != skb->dev->ifindex) + fl6.flowi6_flags |= FLOWI_FLAG_L3MDEV_SRC; dst = icmp6_dst_alloc(skb->dev, &fl6); if (IS_ERR(dst)) { kfree_skb(skb); @@ -767,7 +771,7 @@ static void ndisc_recv_ns(struct sk_buff *skb) ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1); if (ifp) { - +have_ifp: if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) { if (dad) { /* @@ -793,6 +797,18 @@ static void ndisc_recv_ns(struct sk_buff *skb) } else { struct net *net = dev_net(dev); + /* perhaps an address on the master device */ + if (netif_is_l3_slave(dev)) { + struct net_device *mdev; + + mdev = netdev_master_upper_dev_get_rcu(dev); + if (mdev) { + ifp = ipv6_get_ifaddr(net, &msg->target, mdev, 1); + if (ifp) + goto have_ifp; + } + } + idev = in6_dev_get(dev); if (!idev) { /* XXX: count this drop? */ @@ -1484,6 +1500,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) struct flowi6 fl6; int rd_len; u8 ha_buf[MAX_ADDR_LEN], *ha = NULL; + int oif = l3mdev_fib_oif(dev); bool ret; if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) { @@ -1500,7 +1517,10 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) } icmpv6_flow_init(sk, &fl6, NDISC_REDIRECT, - &saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex); + &saddr_buf, &ipv6_hdr(skb)->saddr, oif); + + if (oif != skb->dev->ifindex) + fl6.flowi6_flags |= FLOWI_FLAG_L3MDEV_SRC; dst = ip6_route_output(net, NULL, &fl6); if (dst->error) { diff --git a/net/ipv6/route.c b/net/ipv6/route.c index db5b54a..5fc1149 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -61,6 +61,7 @@ #include #include #include +#include #include @@ -1044,6 +1045,9 @@ static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); saved_fn = fn; + if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF) + oif = 0; + redo_rt6_select: rt = rt6_select(fn, oif, strict); if (rt->rt6i_nsiblings) @@ -1141,7 +1145,7 @@ void ip6_route_input(struct sk_buff *skb) int flags = RT6_LOOKUP_F_HAS_SADDR; struct ip_tunnel_info *tun_info; struct flowi6 fl6 = { - .flowi6_iif = skb->dev->ifindex, + .flowi6_iif = l3mdev_fib_oif(skb->dev), .daddr = iph->daddr, .saddr = iph->saddr, .flowlabel = ip6_flowinfo(iph), @@ -1165,8 +1169,13 @@ static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table struct dst_entry *ip6_route_output(struct net *net, const struct sock *sk, struct flowi6 *fl6) { + struct dst_entry *dst; int flags = 0; + dst = l3mdev_rt6_dst_by_oif(net, fl6); + if (dst) + return dst; + fl6->flowi6_iif = LOOPBACK_IFINDEX; if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) || @@ -2263,7 +2272,6 @@ static struct rt6_info *rt6_add_route_info(struct net *net, unsigned int pref) { struct fib6_config cfg = { - .fc_table = RT6_TABLE_INFO, .fc_metric = IP6_RT_PRIO_USER, .fc_ifindex = ifindex, .fc_dst_len = prefixlen, @@ -2274,6 +2282,7 @@ static struct rt6_info *rt6_add_route_info(struct net *net, .fc_nlinfo.nl_net = net, }; + cfg.fc_table = l3mdev_fib_table_by_index(net, ifindex) ? : RT6_TABLE_INFO; cfg.fc_dst = *prefix; cfg.fc_gateway = *gwaddr; @@ -2314,7 +2323,7 @@ struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, unsigned int pref) { struct fib6_config cfg = { - .fc_table = RT6_TABLE_DFLT, + .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT, .fc_metric = IP6_RT_PRIO_USER, .fc_ifindex = dev->ifindex, .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | @@ -2361,7 +2370,8 @@ static void rtmsg_to_fib6_config(struct net *net, { memset(cfg, 0, sizeof(*cfg)); - cfg->fc_table = RT6_TABLE_MAIN; + cfg->fc_table = l3mdev_fib_table_by_index(net, rtmsg->rtmsg_ifindex) ? + : RT6_TABLE_MAIN; cfg->fc_ifindex = rtmsg->rtmsg_ifindex; cfg->fc_metric = rtmsg->rtmsg_metric; cfg->fc_expires = rtmsg->rtmsg_info; @@ -2470,6 +2480,7 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, const struct in6_addr *addr, bool anycast) { + u32 tb_id; struct net *net = dev_net(idev->dev); struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, DST_NOCOUNT); @@ -2492,7 +2503,8 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, rt->rt6i_gateway = *addr; rt->rt6i_dst.addr = *addr; rt->rt6i_dst.plen = 128; - rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL); + tb_id = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL; + rt->rt6i_table = fib6_get_table(net, tb_id); rt->dst.flags |= DST_NOCACHE; atomic_set(&rt->dst.__refcnt, 1); @@ -3254,6 +3266,11 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) } else { fl6.flowi6_oif = oif; + if (netif_index_is_l3_master(net, oif)) { + fl6.flowi6_flags = FLOWI_FLAG_L3MDEV_SRC | + FLOWI_FLAG_SKIP_NH_OIF; + } + rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6); } -- cgit v0.10.2 From 907b1e6e83ed25d9dece1e55b704581b6c127051 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Mon, 12 Oct 2015 21:47:02 +0200 Subject: bridge: vlan: use proper rcu for the vlgrp member The bridge and port's vlgrp member is already used in RCU way, currently we rely on the fact that it cannot disappear while the port exists but that is error-prone and we might miss places with improper locking (either RCU or RTNL must be held to walk the vlan_list). So make it official and use RCU for vlgrp to catch offenders. Introduce proper vlgrp accessors and use them consistently throughout the code. Signed-off-by: Nikolay Aleksandrov Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index bdfb954..5e88d3e 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -56,7 +56,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) skb_reset_mac_header(skb); skb_pull(skb, ETH_HLEN); - if (!br_allowed_ingress(br, br_vlan_group(br), skb, &vid)) + if (!br_allowed_ingress(br, br_vlan_group_rcu(br), skb, &vid)) goto out; if (is_broadcast_ether_addr(dest)) diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 6d5ed79..a9d424e 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -32,7 +32,7 @@ static inline int should_deliver(const struct net_bridge_port *p, { struct net_bridge_vlan_group *vg; - vg = nbp_vlan_group(p); + vg = nbp_vlan_group_rcu(p); return ((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) && br_allowed_egress(vg, skb) && p->state == BR_STATE_FORWARDING; } @@ -80,7 +80,7 @@ static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb) { struct net_bridge_vlan_group *vg; - vg = nbp_vlan_group(to); + vg = nbp_vlan_group_rcu(to); skb = br_handle_vlan(to->br, vg, skb); if (!skb) return; @@ -112,7 +112,7 @@ static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb) return; } - vg = nbp_vlan_group(to); + vg = nbp_vlan_group_rcu(to); skb = br_handle_vlan(to->br, vg, skb); if (!skb) return; diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index f5c5a45..f7fba74 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -44,7 +44,7 @@ static int br_pass_frame_up(struct sk_buff *skb) brstats->rx_bytes += skb->len; u64_stats_update_end(&brstats->syncp); - vg = br_vlan_group(br); + vg = br_vlan_group_rcu(br); /* Bridge is just like any other port. Make sure the * packet is allowed except in promisc modue when someone * may be running packet capture. @@ -140,7 +140,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb if (!p || p->state == BR_STATE_DISABLED) goto drop; - if (!br_allowed_ingress(p->br, nbp_vlan_group(p), skb, &vid)) + if (!br_allowed_ingress(p->br, nbp_vlan_group_rcu(p), skb, &vid)) goto out; /* insert into forwarding database after filtering to avoid spoofing */ diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index d792d1a..2ee8fd6 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -102,10 +102,10 @@ static size_t br_get_link_af_size_filtered(const struct net_device *dev, rcu_read_lock(); if (br_port_exists(dev)) { p = br_port_get_rcu(dev); - vg = nbp_vlan_group(p); + vg = nbp_vlan_group_rcu(p); } else if (dev->priv_flags & IFF_EBRIDGE) { br = netdev_priv(dev); - vg = br_vlan_group(br); + vg = br_vlan_group_rcu(br); } num_vlan_infos = br_get_num_vlan_infos(vg, filter_mask); rcu_read_unlock(); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index ba0c67b..8835642 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -132,6 +132,7 @@ struct net_bridge_vlan_group { struct list_head vlan_list; u16 num_vlans; u16 pvid; + struct rcu_head rcu; }; struct net_bridge_fdb_entry @@ -229,7 +230,7 @@ struct net_bridge_port struct netpoll *np; #endif #ifdef CONFIG_BRIDGE_VLAN_FILTERING - struct net_bridge_vlan_group *vlgrp; + struct net_bridge_vlan_group __rcu *vlgrp; #endif }; @@ -337,7 +338,7 @@ struct net_bridge struct kobject *ifobj; u32 auto_cnt; #ifdef CONFIG_BRIDGE_VLAN_FILTERING - struct net_bridge_vlan_group *vlgrp; + struct net_bridge_vlan_group __rcu *vlgrp; u8 vlan_enabled; __be16 vlan_proto; u16 default_pvid; @@ -700,13 +701,25 @@ int nbp_get_num_vlan_infos(struct net_bridge_port *p, u32 filter_mask); static inline struct net_bridge_vlan_group *br_vlan_group( const struct net_bridge *br) { - return br->vlgrp; + return rtnl_dereference(br->vlgrp); } static inline struct net_bridge_vlan_group *nbp_vlan_group( const struct net_bridge_port *p) { - return p->vlgrp; + return rtnl_dereference(p->vlgrp); +} + +static inline struct net_bridge_vlan_group *br_vlan_group_rcu( + const struct net_bridge *br) +{ + return rcu_dereference(br->vlgrp); +} + +static inline struct net_bridge_vlan_group *nbp_vlan_group_rcu( + const struct net_bridge_port *p) +{ + return rcu_dereference(p->vlgrp); } /* Since bridge now depends on 8021Q module, but the time bridge sees the @@ -853,6 +866,19 @@ static inline struct net_bridge_vlan_group *nbp_vlan_group( { return NULL; } + +static inline struct net_bridge_vlan_group *br_vlan_group_rcu( + const struct net_bridge *br) +{ + return NULL; +} + +static inline struct net_bridge_vlan_group *nbp_vlan_group_rcu( + const struct net_bridge_port *p) +{ + return NULL; +} + #endif struct nf_br_ops { diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index ad7e4f6..ffaa6d9 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -54,9 +54,9 @@ static void __vlan_add_flags(struct net_bridge_vlan *v, u16 flags) struct net_bridge_vlan_group *vg; if (br_vlan_is_master(v)) - vg = v->br->vlgrp; + vg = br_vlan_group(v->br); else - vg = v->port->vlgrp; + vg = nbp_vlan_group(v->port); if (flags & BRIDGE_VLAN_INFO_PVID) __vlan_add_pvid(vg, v->vid); @@ -91,11 +91,16 @@ static int __vlan_vid_add(struct net_device *dev, struct net_bridge *br, static void __vlan_add_list(struct net_bridge_vlan *v) { + struct net_bridge_vlan_group *vg; struct list_head *headp, *hpos; struct net_bridge_vlan *vent; - headp = br_vlan_is_master(v) ? &v->br->vlgrp->vlan_list : - &v->port->vlgrp->vlan_list; + if (br_vlan_is_master(v)) + vg = br_vlan_group(v->br); + else + vg = nbp_vlan_group(v->port); + + headp = &vg->vlan_list; list_for_each_prev(hpos, headp) { vent = list_entry(hpos, struct net_bridge_vlan, vlist); if (v->vid < vent->vid) @@ -137,14 +142,16 @@ static int __vlan_vid_del(struct net_device *dev, struct net_bridge *br, */ static struct net_bridge_vlan *br_vlan_get_master(struct net_bridge *br, u16 vid) { + struct net_bridge_vlan_group *vg; struct net_bridge_vlan *masterv; - masterv = br_vlan_find(br->vlgrp, vid); + vg = br_vlan_group(br); + masterv = br_vlan_find(vg, vid); if (!masterv) { /* missing global ctx, create it now */ if (br_vlan_add(br, vid, 0)) return NULL; - masterv = br_vlan_find(br->vlgrp, vid); + masterv = br_vlan_find(vg, vid); if (WARN_ON(!masterv)) return NULL; } @@ -155,11 +162,14 @@ static struct net_bridge_vlan *br_vlan_get_master(struct net_bridge *br, u16 vid static void br_vlan_put_master(struct net_bridge_vlan *masterv) { + struct net_bridge_vlan_group *vg; + if (!br_vlan_is_master(masterv)) return; + vg = br_vlan_group(masterv->br); if (atomic_dec_and_test(&masterv->refcnt)) { - rhashtable_remove_fast(&masterv->br->vlgrp->vlan_hash, + rhashtable_remove_fast(&vg->vlan_hash, &masterv->vnode, br_vlan_rht_params); __vlan_del_list(masterv); kfree_rcu(masterv, rcu); @@ -189,12 +199,12 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags) if (br_vlan_is_master(v)) { br = v->br; dev = br->dev; - vg = br->vlgrp; + vg = br_vlan_group(br); } else { p = v->port; br = p->br; dev = p->dev; - vg = p->vlgrp; + vg = nbp_vlan_group(p); } if (p) { @@ -266,10 +276,10 @@ static int __vlan_del(struct net_bridge_vlan *v) int err = 0; if (br_vlan_is_master(v)) { - vg = v->br->vlgrp; + vg = br_vlan_group(v->br); } else { p = v->port; - vg = v->port->vlgrp; + vg = nbp_vlan_group(v->port); masterv = v->brvlan; } @@ -305,7 +315,7 @@ static void __vlan_flush(struct net_bridge_vlan_group *vlgrp) list_for_each_entry_safe(vlan, tmp, &vlgrp->vlan_list, vlist) __vlan_del(vlan); rhashtable_destroy(&vlgrp->vlan_hash); - kfree(vlgrp); + kfree_rcu(vlgrp, rcu); } struct sk_buff *br_handle_vlan(struct net_bridge *br, @@ -467,7 +477,7 @@ bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid) if (!br->vlan_enabled) return true; - vg = p->vlgrp; + vg = nbp_vlan_group(p); if (!vg || !vg->num_vlans) return false; @@ -493,12 +503,14 @@ bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid) */ int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags) { + struct net_bridge_vlan_group *vg; struct net_bridge_vlan *vlan; int ret; ASSERT_RTNL(); - vlan = br_vlan_find(br->vlgrp, vid); + vg = br_vlan_group(br); + vlan = br_vlan_find(vg, vid); if (vlan) { if (!br_vlan_is_brentry(vlan)) { /* Trying to change flags of non-existent bridge vlan */ @@ -513,7 +525,7 @@ int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags) } atomic_inc(&vlan->refcnt); vlan->flags |= BRIDGE_VLAN_INFO_BRENTRY; - br->vlgrp->num_vlans++; + vg->num_vlans++; } __vlan_add_flags(vlan, flags); return 0; @@ -541,11 +553,13 @@ int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags) */ int br_vlan_delete(struct net_bridge *br, u16 vid) { + struct net_bridge_vlan_group *vg; struct net_bridge_vlan *v; ASSERT_RTNL(); - v = br_vlan_find(br->vlgrp, vid); + vg = br_vlan_group(br); + v = br_vlan_find(vg, vid); if (!v || !br_vlan_is_brentry(v)) return -ENOENT; @@ -626,6 +640,7 @@ int __br_vlan_set_proto(struct net_bridge *br, __be16 proto) int err = 0; struct net_bridge_port *p; struct net_bridge_vlan *vlan; + struct net_bridge_vlan_group *vg; __be16 oldproto; if (br->vlan_proto == proto) @@ -633,7 +648,8 @@ int __br_vlan_set_proto(struct net_bridge *br, __be16 proto) /* Add VLANs for the new proto to the device filter. */ list_for_each_entry(p, &br->port_list, list) { - list_for_each_entry(vlan, &p->vlgrp->vlan_list, vlist) { + vg = nbp_vlan_group(p); + list_for_each_entry(vlan, &vg->vlan_list, vlist) { err = vlan_vid_add(p->dev, proto, vlan->vid); if (err) goto err_filt; @@ -647,19 +663,23 @@ int __br_vlan_set_proto(struct net_bridge *br, __be16 proto) br_recalculate_fwd_mask(br); /* Delete VLANs for the old proto from the device filter. */ - list_for_each_entry(p, &br->port_list, list) - list_for_each_entry(vlan, &p->vlgrp->vlan_list, vlist) + list_for_each_entry(p, &br->port_list, list) { + vg = nbp_vlan_group(p); + list_for_each_entry(vlan, &vg->vlan_list, vlist) vlan_vid_del(p->dev, oldproto, vlan->vid); + } return 0; err_filt: - list_for_each_entry_continue_reverse(vlan, &p->vlgrp->vlan_list, vlist) + list_for_each_entry_continue_reverse(vlan, &vg->vlan_list, vlist) vlan_vid_del(p->dev, proto, vlan->vid); - list_for_each_entry_continue_reverse(p, &br->port_list, list) - list_for_each_entry(vlan, &p->vlgrp->vlan_list, vlist) + list_for_each_entry_continue_reverse(p, &br->port_list, list) { + vg = nbp_vlan_group(p); + list_for_each_entry(vlan, &vg->vlan_list, vlist) vlan_vid_del(p->dev, proto, vlan->vid); + } return err; } @@ -703,11 +723,11 @@ static void br_vlan_disable_default_pvid(struct net_bridge *br) /* Disable default_pvid on all ports where it is still * configured. */ - if (vlan_default_pvid(br->vlgrp, pvid)) + if (vlan_default_pvid(br_vlan_group(br), pvid)) br_vlan_delete(br, pvid); list_for_each_entry(p, &br->port_list, list) { - if (vlan_default_pvid(p->vlgrp, pvid)) + if (vlan_default_pvid(nbp_vlan_group(p), pvid)) nbp_vlan_delete(p, pvid); } @@ -717,6 +737,7 @@ static void br_vlan_disable_default_pvid(struct net_bridge *br) int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid) { const struct net_bridge_vlan *pvent; + struct net_bridge_vlan_group *vg; struct net_bridge_port *p; u16 old_pvid; int err = 0; @@ -737,8 +758,9 @@ int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid) /* Update default_pvid config only if we do not conflict with * user configuration. */ - pvent = br_vlan_find(br->vlgrp, pvid); - if ((!old_pvid || vlan_default_pvid(br->vlgrp, old_pvid)) && + vg = br_vlan_group(br); + pvent = br_vlan_find(vg, pvid); + if ((!old_pvid || vlan_default_pvid(vg, old_pvid)) && (!pvent || !br_vlan_should_use(pvent))) { err = br_vlan_add(br, pvid, BRIDGE_VLAN_INFO_PVID | @@ -754,9 +776,10 @@ int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid) /* Update default_pvid config only if we do not conflict with * user configuration. */ + vg = nbp_vlan_group(p); if ((old_pvid && - !vlan_default_pvid(p->vlgrp, old_pvid)) || - br_vlan_find(p->vlgrp, pvid)) + !vlan_default_pvid(vg, old_pvid)) || + br_vlan_find(vg, pvid)) continue; err = nbp_vlan_add(p, pvid, @@ -825,17 +848,19 @@ unlock: int br_vlan_init(struct net_bridge *br) { + struct net_bridge_vlan_group *vg; int ret = -ENOMEM; - br->vlgrp = kzalloc(sizeof(struct net_bridge_vlan_group), GFP_KERNEL); - if (!br->vlgrp) + vg = kzalloc(sizeof(*vg), GFP_KERNEL); + if (!vg) goto out; - ret = rhashtable_init(&br->vlgrp->vlan_hash, &br_vlan_rht_params); + ret = rhashtable_init(&vg->vlan_hash, &br_vlan_rht_params); if (ret) goto err_rhtbl; - INIT_LIST_HEAD(&br->vlgrp->vlan_list); + INIT_LIST_HEAD(&vg->vlan_list); br->vlan_proto = htons(ETH_P_8021Q); br->default_pvid = 1; + rcu_assign_pointer(br->vlgrp, vg); ret = br_vlan_add(br, 1, BRIDGE_VLAN_INFO_PVID | BRIDGE_VLAN_INFO_UNTAGGED | BRIDGE_VLAN_INFO_BRENTRY); @@ -846,9 +871,9 @@ out: return ret; err_vlan_add: - rhashtable_destroy(&br->vlgrp->vlan_hash); + rhashtable_destroy(&vg->vlan_hash); err_rhtbl: - kfree(br->vlgrp); + kfree(vg); goto out; } @@ -866,9 +891,7 @@ int nbp_vlan_init(struct net_bridge_port *p) if (ret) goto err_rhtbl; INIT_LIST_HEAD(&vg->vlan_list); - /* Make sure everything's committed before publishing vg */ - smp_wmb(); - p->vlgrp = vg; + rcu_assign_pointer(p->vlgrp, vg); if (p->br->default_pvid) { ret = nbp_vlan_add(p, p->br->default_pvid, BRIDGE_VLAN_INFO_PVID | @@ -897,7 +920,7 @@ int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags) ASSERT_RTNL(); - vlan = br_vlan_find(port->vlgrp, vid); + vlan = br_vlan_find(nbp_vlan_group(port), vid); if (vlan) { __vlan_add_flags(vlan, flags); return 0; @@ -925,7 +948,7 @@ int nbp_vlan_delete(struct net_bridge_port *port, u16 vid) ASSERT_RTNL(); - v = br_vlan_find(port->vlgrp, vid); + v = br_vlan_find(nbp_vlan_group(port), vid); if (!v) return -ENOENT; br_fdb_find_delete_local(port->br, port, port->dev->dev_addr, vid); @@ -936,12 +959,14 @@ int nbp_vlan_delete(struct net_bridge_port *port, u16 vid) void nbp_vlan_flush(struct net_bridge_port *port) { + struct net_bridge_vlan_group *vg; struct net_bridge_vlan *vlan; ASSERT_RTNL(); - list_for_each_entry(vlan, &port->vlgrp->vlan_list, vlist) + vg = nbp_vlan_group(port); + list_for_each_entry(vlan, &vg->vlan_list, vlist) vlan_vid_del(port->dev, port->br->vlan_proto, vlan->vid); - __vlan_flush(nbp_vlan_group(port)); + __vlan_flush(vg); } -- cgit v0.10.2 From e9c953eff7f0ec69a52cfa87b912ab48902a0314 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Mon, 12 Oct 2015 21:47:03 +0200 Subject: bridge: vlan: use rcu for vlan_list traversal in br_fill_ifinfo br_fill_ifinfo is called by br_ifinfo_notify which can be called from many contexts with different locks held, sometimes it relies upon bridge's spinlock only which is a problem for the vlan code, so use explicitly rcu for that to avoid problems. Signed-off-by: Nikolay Aleksandrov Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 2ee8fd6..94b4de8 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -253,7 +253,7 @@ static int br_fill_ifvlaninfo_compressed(struct sk_buff *skb, * if vlaninfo represents a range */ pvid = br_get_pvid(vg); - list_for_each_entry(v, &vg->vlan_list, vlist) { + list_for_each_entry_rcu(v, &vg->vlan_list, vlist) { flags = 0; if (!br_vlan_should_use(v)) continue; @@ -303,7 +303,7 @@ static int br_fill_ifvlaninfo(struct sk_buff *skb, u16 pvid; pvid = br_get_pvid(vg); - list_for_each_entry(v, &vg->vlan_list, vlist) { + list_for_each_entry_rcu(v, &vg->vlan_list, vlist) { if (!br_vlan_should_use(v)) continue; @@ -386,22 +386,27 @@ static int br_fill_ifinfo(struct sk_buff *skb, struct nlattr *af; int err; + /* RCU needed because of the VLAN locking rules (rcu || rtnl) */ + rcu_read_lock(); if (port) - vg = nbp_vlan_group(port); + vg = nbp_vlan_group_rcu(port); else - vg = br_vlan_group(br); + vg = br_vlan_group_rcu(br); - if (!vg || !vg->num_vlans) + if (!vg || !vg->num_vlans) { + rcu_read_unlock(); goto done; - + } af = nla_nest_start(skb, IFLA_AF_SPEC); - if (!af) + if (!af) { + rcu_read_unlock(); goto nla_put_failure; - + } if (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED) err = br_fill_ifvlaninfo_compressed(skb, vg); else err = br_fill_ifvlaninfo(skb, vg); + rcu_read_unlock(); if (err) goto nla_put_failure; nla_nest_end(skb, af); -- cgit v0.10.2 From b8d02c3cace37393bf9ff0a9eaa1ee39cda1d259 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Mon, 12 Oct 2015 21:47:04 +0200 Subject: bridge: vlan: drop unnecessary flush code As Ido Schimmel pointed out the vlan_vid_del() code in nbp_vlan_flush is unnecessary (and is actually a remnant of the old vlan code) so we can remove it. Signed-off-by: Nikolay Aleksandrov Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index ffaa6d9..85e6756 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -959,14 +959,7 @@ int nbp_vlan_delete(struct net_bridge_port *port, u16 vid) void nbp_vlan_flush(struct net_bridge_port *port) { - struct net_bridge_vlan_group *vg; - struct net_bridge_vlan *vlan; - ASSERT_RTNL(); - vg = nbp_vlan_group(port); - list_for_each_entry(vlan, &vg->vlan_list, vlist) - vlan_vid_del(port->dev, port->br->vlan_proto, vlan->vid); - - __vlan_flush(vg); + __vlan_flush(nbp_vlan_group(port)); } -- cgit v0.10.2 From f409d0ed87d2721e1099ce36266e98c5aea2d486 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Mon, 12 Oct 2015 21:47:05 +0200 Subject: bridge: vlan: move back vlan_flush Ido Schimmel reported a problem with switchdev devices because of the order change of del_nbp operations, more specifically the move of nbp_vlan_flush() which deletes all vlans and frees vlgrp after the rx_handler has been unregistered. So in order to fix this move vlan_flush back where it was and make it destroy the rhtable after NULLing vlgrp and waiting a grace period to make sure noone can see it. Reported-by: Ido Schimmel Signed-off-by: Nikolay Aleksandrov Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 934cae9..45e4757 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -248,6 +248,7 @@ static void del_nbp(struct net_bridge_port *p) list_del_rcu(&p->list); + nbp_vlan_flush(p); br_fdb_delete_by_port(br, p, 0, 1); nbp_update_port_count(br); @@ -256,8 +257,6 @@ static void del_nbp(struct net_bridge_port *p) dev->priv_flags &= ~IFF_BRIDGE_PORT; netdev_rx_handler_unregister(dev); - /* use the synchronize_rcu done by netdev_rx_handler_unregister */ - nbp_vlan_flush(p); br_multicast_del_port(p); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 8835642..216018c 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -132,7 +132,6 @@ struct net_bridge_vlan_group { struct list_head vlan_list; u16 num_vlans; u16 pvid; - struct rcu_head rcu; }; struct net_bridge_fdb_entry diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 85e6756..5f0d0cc 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -307,15 +307,20 @@ out: return err; } -static void __vlan_flush(struct net_bridge_vlan_group *vlgrp) +static void __vlan_group_free(struct net_bridge_vlan_group *vg) +{ + WARN_ON(!list_empty(&vg->vlan_list)); + rhashtable_destroy(&vg->vlan_hash); + kfree(vg); +} + +static void __vlan_flush(struct net_bridge_vlan_group *vg) { struct net_bridge_vlan *vlan, *tmp; - __vlan_delete_pvid(vlgrp, vlgrp->pvid); - list_for_each_entry_safe(vlan, tmp, &vlgrp->vlan_list, vlist) + __vlan_delete_pvid(vg, vg->pvid); + list_for_each_entry_safe(vlan, tmp, &vg->vlan_list, vlist) __vlan_del(vlan); - rhashtable_destroy(&vlgrp->vlan_hash); - kfree_rcu(vlgrp, rcu); } struct sk_buff *br_handle_vlan(struct net_bridge *br, @@ -571,9 +576,15 @@ int br_vlan_delete(struct net_bridge *br, u16 vid) void br_vlan_flush(struct net_bridge *br) { + struct net_bridge_vlan_group *vg; + ASSERT_RTNL(); - __vlan_flush(br_vlan_group(br)); + vg = br_vlan_group(br); + __vlan_flush(vg); + RCU_INIT_POINTER(br->vlgrp, NULL); + synchronize_rcu(); + __vlan_group_free(vg); } struct net_bridge_vlan *br_vlan_find(struct net_bridge_vlan_group *vg, u16 vid) @@ -959,7 +970,13 @@ int nbp_vlan_delete(struct net_bridge_port *port, u16 vid) void nbp_vlan_flush(struct net_bridge_port *port) { + struct net_bridge_vlan_group *vg; + ASSERT_RTNL(); - __vlan_flush(nbp_vlan_group(port)); + vg = nbp_vlan_group(port); + __vlan_flush(vg); + RCU_INIT_POINTER(port->vlgrp, NULL); + synchronize_rcu(); + __vlan_group_free(vg); } -- cgit v0.10.2 From 887e07be3fd2d056362a9f851f796580d7bca499 Mon Sep 17 00:00:00 2001 From: Gerhard Bertelsmann Date: Fri, 25 Sep 2015 18:58:38 +0200 Subject: can: sun4i: fix arbitration lost error reporting This patch fixes a bug in arbitration error reporting Reported-by: Dan Carpenter Signed-off-by: Gerhard Bertelsmann Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/sun4i_can.c b/drivers/net/can/sun4i_can.c index 10d8497..67bdf6d 100644 --- a/drivers/net/can/sun4i_can.c +++ b/drivers/net/can/sun4i_can.c @@ -601,7 +601,7 @@ static int sun4i_can_err(struct net_device *dev, u8 isrc, u8 status) stats->tx_errors++; if (likely(skb)) { cf->can_id |= CAN_ERR_LOSTARB; - cf->data[0] = (alc & 0x1f) >> 8; + cf->data[0] = (alc >> 8) & 0x1f; } } -- cgit v0.10.2 From 3c200db56441365d964b5a983de948821f5011b9 Mon Sep 17 00:00:00 2001 From: Gerhard Bertelsmann Date: Fri, 25 Sep 2015 18:58:39 +0200 Subject: can: sun4i: fix MODULE_DESCRIPTION This patch change description of the module. Signed-off-by: Gerhard Bertelsmann Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/sun4i_can.c b/drivers/net/can/sun4i_can.c index 67bdf6d..d9a42c6 100644 --- a/drivers/net/can/sun4i_can.c +++ b/drivers/net/can/sun4i_can.c @@ -854,4 +854,4 @@ module_platform_driver(sun4i_can_driver); MODULE_AUTHOR("Peter Chen "); MODULE_AUTHOR("Gerhard Bertelsmann "); MODULE_LICENSE("Dual BSD/GPL"); -MODULE_DESCRIPTION(DRV_NAME "CAN driver for Allwinner SoCs (A10/A20)"); +MODULE_DESCRIPTION("CAN driver for Allwinner SoCs (A10/A20)"); -- cgit v0.10.2 From ba61a8d9d780980e8284355a0be750897e7af212 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 30 Sep 2015 13:26:42 +0200 Subject: can: avoid using timeval for uapi The can subsystem communicates with user space using a bcm_msg_head header, which contains two timestamps. This is problematic for multiple reasons: a) The structure layout is currently incompatible between 64-bit user space and 32-bit user space, and cannot work in compat mode (other than x32). b) The timeval structure layout will change in 32-bit user space when we fix the y2038 overflow problem by redefining time_t to 64-bit, making new 32-bit user space incompatible with the current kernel interface. Cars last a long time and often use old kernels, so the actual users of this code are the most likely ones to migrate to y2038 safe user space. This tries to work around part of the problem by changing the publicly visible user interface in the header, but not the binary interface. Fortunately, the values passed around in the structure are relative times and do not actually suffer from the y2038 overflow, so 32-bit is enough here. We replace the use of 'struct timeval' with a newly defined 'struct bcm_timeval' that uses the exact same binary layout as before and that still suffers from problem a) but not problem b). The downside of this approach is that any user space program that currently assigns a timeval structure to these members rather than writing the tv_sec/tv_usec portions individually will suffer a compile-time error when built with an updated kernel header. Fixing this error makes it work fine with old and new headers though. We could address problem a) by using '__u32' or 'int' members rather than 'long', but that would have a more significant downside in also breaking support for all existing 64-bit user binaries that might be using this interface, which is likely not acceptable. Signed-off-by: Arnd Bergmann Acked-by: Oliver Hartkopp Cc: linux-can@vger.kernel.org Cc: linux-api@vger.kernel.org Signed-off-by: Marc Kleine-Budde diff --git a/include/uapi/linux/can/bcm.h b/include/uapi/linux/can/bcm.h index 89ddb9d..7a291dc 100644 --- a/include/uapi/linux/can/bcm.h +++ b/include/uapi/linux/can/bcm.h @@ -47,6 +47,11 @@ #include #include +struct bcm_timeval { + long tv_sec; + long tv_usec; +}; + /** * struct bcm_msg_head - head of messages to/from the broadcast manager * @opcode: opcode, see enum below. @@ -62,7 +67,7 @@ struct bcm_msg_head { __u32 opcode; __u32 flags; __u32 count; - struct timeval ival1, ival2; + struct bcm_timeval ival1, ival2; canid_t can_id; __u32 nframes; struct can_frame frames[0]; diff --git a/net/can/bcm.c b/net/can/bcm.c index a1ba687..6863310 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -96,7 +96,7 @@ struct bcm_op { canid_t can_id; u32 flags; unsigned long frames_abs, frames_filtered; - struct timeval ival1, ival2; + struct bcm_timeval ival1, ival2; struct hrtimer timer, thrtimer; struct tasklet_struct tsklet, thrtsklet; ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg; @@ -131,6 +131,11 @@ static inline struct bcm_sock *bcm_sk(const struct sock *sk) return (struct bcm_sock *)sk; } +static inline ktime_t bcm_timeval_to_ktime(struct bcm_timeval tv) +{ + return ktime_set(tv.tv_sec, tv.tv_usec * NSEC_PER_USEC); +} + #define CFSIZ sizeof(struct can_frame) #define OPSIZ sizeof(struct bcm_op) #define MHSIZ sizeof(struct bcm_msg_head) @@ -953,8 +958,8 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, op->count = msg_head->count; op->ival1 = msg_head->ival1; op->ival2 = msg_head->ival2; - op->kt_ival1 = timeval_to_ktime(msg_head->ival1); - op->kt_ival2 = timeval_to_ktime(msg_head->ival2); + op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1); + op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2); /* disable an active timer due to zero values? */ if (!op->kt_ival1.tv64 && !op->kt_ival2.tv64) @@ -1134,8 +1139,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* set timer value */ op->ival1 = msg_head->ival1; op->ival2 = msg_head->ival2; - op->kt_ival1 = timeval_to_ktime(msg_head->ival1); - op->kt_ival2 = timeval_to_ktime(msg_head->ival2); + op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1); + op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2); /* disable an active timer due to zero value? */ if (!op->kt_ival1.tv64) -- cgit v0.10.2 From 42160a041db89807691b2a3fbf42e36a98b6019e Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 8 Oct 2015 16:56:07 +0200 Subject: can: at91: remove at91_can_data struct at91_can_data was used to pass a callback to the driver, allowing it to switch the transceiver on and off. As all at91 boards are now using DT, this is not used anymore, remove that structure. Signed-off-by: Alexandre Belloni Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c index 945c095..8b3275d 100644 --- a/drivers/net/can/at91_can.c +++ b/drivers/net/can/at91_can.c @@ -8,15 +8,6 @@ * Public License ("GPL") version 2 as distributed in the 'COPYING' * file from the main directory of the linux kernel source. * - * - * Your platform definition file should specify something like: - * - * static struct at91_can_data ek_can_data = { - * transceiver_switch = sam9263ek_transceiver_switch, - * }; - * - * at91_add_device_can(&ek_can_data); - * */ #include @@ -33,7 +24,6 @@ #include #include #include -#include #include #include @@ -324,15 +314,6 @@ static inline u32 at91_can_id_to_reg_mid(canid_t can_id) return reg_mid; } -/* - * Swtich transceiver on or off - */ -static void at91_transceiver_switch(const struct at91_priv *priv, int on) -{ - if (priv->pdata && priv->pdata->transceiver_switch) - priv->pdata->transceiver_switch(on); -} - static void at91_setup_mailboxes(struct net_device *dev) { struct at91_priv *priv = netdev_priv(dev); @@ -416,7 +397,6 @@ static void at91_chip_start(struct net_device *dev) at91_set_bittiming(dev); at91_setup_mailboxes(dev); - at91_transceiver_switch(priv, 1); /* enable chip */ if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) @@ -444,7 +424,6 @@ static void at91_chip_stop(struct net_device *dev, enum can_state state) reg_mr = at91_read(priv, AT91_MR); at91_write(priv, AT91_MR, reg_mr & ~AT91_MR_CANEN); - at91_transceiver_switch(priv, 0); priv->can.state = state; } diff --git a/include/linux/platform_data/atmel.h b/include/linux/platform_data/atmel.h index 527a85c..c121ddf 100644 --- a/include/linux/platform_data/atmel.h +++ b/include/linux/platform_data/atmel.h @@ -74,11 +74,6 @@ struct atmel_uart_data { struct serial_rs485 rs485; /* rs485 settings */ }; -/* CAN */ -struct at91_can_data { - void (*transceiver_switch)(int on); -}; - /* FIXME: this needs a better location, but gets stuff building again */ extern int at91_suspend_entering_slow_clock(void); -- cgit v0.10.2 From 4bdc3d66147b3a623b32216a45431d0cff005f50 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 13 Oct 2015 17:12:54 -0700 Subject: tcp/dccp: fix behavior of stale SYN_RECV request sockets When a TCP/DCCP listener is closed, its pending SYN_RECV request sockets become stale, meaning 3WHS can not complete. But current behavior is wrong : incoming packets finding such stale sockets are dropped. We need instead to cleanup the request socket and perform another lookup : - Incoming ACK will give a RST answer, - SYN rtx might find another listener if available. - We expedite cleanup of request sockets and old listener socket. Fixes: 079096f103fa ("tcp/dccp: install syn_recv requests into ehash table") Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 8e99681..0dcf196 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -799,15 +799,10 @@ static int dccp_v4_rcv(struct sk_buff *skb) DCCP_SKB_CB(skb)->dccpd_ack_seq); } - /* Step 2: - * Look up flow ID in table and get corresponding socket */ +lookup: sk = __inet_lookup_skb(&dccp_hashinfo, skb, dh->dccph_sport, dh->dccph_dport); - /* - * Step 2: - * If no socket ... - */ - if (sk == NULL) { + if (!sk) { dccp_pr_debug("failed to look up flow ID in table and " "get corresponding socket\n"); goto no_dccp_socket; @@ -830,8 +825,12 @@ static int dccp_v4_rcv(struct sk_buff *skb) struct sock *nsk = NULL; sk = req->rsk_listener; - if (sk->sk_state == DCCP_LISTEN) + if (likely(sk->sk_state == DCCP_LISTEN)) { nsk = dccp_check_req(sk, skb, req); + } else { + inet_csk_reqsk_queue_drop(sk, req); + goto lookup; + } if (!nsk) { reqsk_put(req); goto discard_it; diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index aed314f..6883193 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -656,16 +656,11 @@ static int dccp_v6_rcv(struct sk_buff *skb) else DCCP_SKB_CB(skb)->dccpd_ack_seq = dccp_hdr_ack_seq(skb); - /* Step 2: - * Look up flow ID in table and get corresponding socket */ +lookup: sk = __inet6_lookup_skb(&dccp_hashinfo, skb, dh->dccph_sport, dh->dccph_dport, inet6_iif(skb)); - /* - * Step 2: - * If no socket ... - */ - if (sk == NULL) { + if (!sk) { dccp_pr_debug("failed to look up flow ID in table and " "get corresponding socket\n"); goto no_dccp_socket; @@ -688,8 +683,12 @@ static int dccp_v6_rcv(struct sk_buff *skb) struct sock *nsk = NULL; sk = req->rsk_listener; - if (sk->sk_state == DCCP_LISTEN) + if (likely(sk->sk_state == DCCP_LISTEN)) { nsk = dccp_check_req(sk, skb, req); + } else { + inet_csk_reqsk_queue_drop(sk, req); + goto lookup; + } if (!nsk) { reqsk_put(req); goto discard_it; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index ddb1983..1ff0923 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1572,6 +1572,7 @@ int tcp_v4_rcv(struct sk_buff *skb) TCP_SKB_CB(skb)->ip_dsfield = ipv4_get_dsfield(iph); TCP_SKB_CB(skb)->sacked = 0; +lookup: sk = __inet_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest); if (!sk) goto no_tcp_socket; @@ -1587,8 +1588,12 @@ process: sk = req->rsk_listener; if (tcp_v4_inbound_md5_hash(sk, skb)) goto discard_and_relse; - if (sk->sk_state == TCP_LISTEN) + if (likely(sk->sk_state == TCP_LISTEN)) { nsk = tcp_check_req(sk, skb, req, false); + } else { + inet_csk_reqsk_queue_drop(sk, req); + goto lookup; + } if (!nsk) { reqsk_put(req); goto discard_it; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 2887c84..7ce1c57 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1363,6 +1363,7 @@ static int tcp_v6_rcv(struct sk_buff *skb) th = tcp_hdr(skb); hdr = ipv6_hdr(skb); +lookup: sk = __inet6_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest, inet6_iif(skb)); if (!sk) @@ -1382,8 +1383,12 @@ process: reqsk_put(req); goto discard_it; } - if (sk->sk_state == TCP_LISTEN) + if (likely(sk->sk_state == TCP_LISTEN)) { nsk = tcp_check_req(sk, skb, req, false); + } else { + inet_csk_reqsk_queue_drop(sk, req); + goto lookup; + } if (!nsk) { reqsk_put(req); goto discard_it; -- cgit v0.10.2 From c3643885aa8a3c7cff3ba3269337f39361d76f76 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Mon, 12 Oct 2015 01:19:07 -0700 Subject: mISDN: use kstrdup() in dsp_pipeline_build Use kstrdup instead of strlen-kmalloc-strcpy. Remove unneeded NULL test, it will be tested inside kstrdup. Remove 0 length string test, it has been tested in the caller of dsp_pipeline_build. Signed-off-by: Geliang Tang Signed-off-by: David S. Miller diff --git a/drivers/isdn/mISDN/dsp_pipeline.c b/drivers/isdn/mISDN/dsp_pipeline.c index 8b1a66c..e72b4e7 100644 --- a/drivers/isdn/mISDN/dsp_pipeline.c +++ b/drivers/isdn/mISDN/dsp_pipeline.c @@ -235,7 +235,7 @@ void dsp_pipeline_destroy(struct dsp_pipeline *pipeline) int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg) { - int len, incomplete = 0, found = 0; + int incomplete = 0, found = 0; char *dup, *tok, *name, *args; struct dsp_element_entry *entry, *n; struct dsp_pipeline_entry *pipeline_entry; @@ -247,17 +247,9 @@ int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg) if (!list_empty(&pipeline->list)) _dsp_pipeline_destroy(pipeline); - if (!cfg) - return 0; - - len = strlen(cfg); - if (!len) - return 0; - - dup = kmalloc(len + 1, GFP_ATOMIC); + dup = kstrdup(cfg, GFP_ATOMIC); if (!dup) return 0; - strcpy(dup, cfg); while ((tok = strsep(&dup, "|"))) { if (!strlen(tok)) continue; -- cgit v0.10.2 From 4b418bff3dae0c017126220df3e148ba8127e99a Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 12 Oct 2015 13:54:38 -0700 Subject: net: vrf: Documentation update, ip commands Add ip commands with examples for creating VRF devics, enslaving interfaces and dumping VRF-focused data (address, neighbors, routes). Signed-off-by: David Ahern Signed-off-by: David S. Miller diff --git a/Documentation/networking/vrf.txt b/Documentation/networking/vrf.txt index 031ef4a..d52aa10 100644 --- a/Documentation/networking/vrf.txt +++ b/Documentation/networking/vrf.txt @@ -90,7 +90,304 @@ or to specify the output device using cmsg and IP_PKTINFO. Limitations ----------- -VRF device currently only works for IPv4. Support for IPv6 is under development. - Index of original ingress interface is not available via cmsg. Will address soon. + +################################################################################ + +Using iproute2 for VRFs +======================= +VRF devices do *not* have to start with 'vrf-'. That is a convention used here +for emphasis of the device type, similar to use of 'br' in bridge names. + +1. Create a VRF + + To instantiate a VRF device and associate it with a table: + $ ip link add dev NAME type vrf table ID + + Remember to add the ip rules as well: + $ ip ru add oif NAME table 10 + $ ip ru add iif NAME table 10 + $ ip -6 ru add oif NAME table 10 + $ ip -6 ru add iif NAME table 10 + + Without the rules route lookups are not directed to the table. + + For example: + $ ip link add dev vrf-blue type vrf table 10 + $ ip ru add pref 200 oif vrf-blue table 10 + $ ip ru add pref 200 iif vrf-blue table 10 + $ ip -6 ru add pref 200 oif vrf-blue table 10 + $ ip -6 ru add pref 200 iif vrf-blue table 10 + + +2. List VRFs + + To list VRFs that have been created: + $ ip [-d] link show type vrf + NOTE: The -d option is needed to show the table id + + For example: + $ ip -d link show type vrf + 11: vrf-mgmt: mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 + link/ether 72:b3:ba:91:e2:24 brd ff:ff:ff:ff:ff:ff promiscuity 0 + vrf table 1 addrgenmode eui64 + 12: vrf-red: mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 + link/ether b6:6f:6e:f6:da:73 brd ff:ff:ff:ff:ff:ff promiscuity 0 + vrf table 10 addrgenmode eui64 + 13: vrf-blue: mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 + link/ether 36:62:e8:7d:bb:8c brd ff:ff:ff:ff:ff:ff promiscuity 0 + vrf table 66 addrgenmode eui64 + 14: vrf-green: mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 + link/ether e6:28:b8:63:70:bb brd ff:ff:ff:ff:ff:ff promiscuity 0 + vrf table 81 addrgenmode eui64 + + + Or in brief output: + + $ ip -br link show type vrf + vrf-mgmt UP 72:b3:ba:91:e2:24 + vrf-red UP b6:6f:6e:f6:da:73 + vrf-blue UP 36:62:e8:7d:bb:8c + vrf-green UP e6:28:b8:63:70:bb + + +3. Assign a Network Interface to a VRF + + Network interfaces are assigned to a VRF by enslaving the netdevice to a + VRF device: + $ ip link set dev NAME master VRF-NAME + + On enslavement connected and local routes are automatically moved to the + table associated with the VRF device. + + For example: + $ ip link set dev eth0 master vrf-mgmt + + +4. Show Devices Assigned to a VRF + + To show devices that have been assigned to a specific VRF add the master + option to the ip command: + $ ip link show master VRF-NAME + + For example: + $ ip link show master vrf-red + 3: eth1: mtu 1500 qdisc pfifo_fast master vrf-red state UP mode DEFAULT group default qlen 1000 + link/ether 02:00:00:00:02:02 brd ff:ff:ff:ff:ff:ff + 4: eth2: mtu 1500 qdisc pfifo_fast master vrf-red state UP mode DEFAULT group default qlen 1000 + link/ether 02:00:00:00:02:03 brd ff:ff:ff:ff:ff:ff + 7: eth5: mtu 1500 qdisc noop master vrf-red state DOWN mode DEFAULT group default qlen 1000 + link/ether 02:00:00:00:02:06 brd ff:ff:ff:ff:ff:ff + + + Or using the brief output: + $ ip -br link show master vrf-red + eth1 UP 02:00:00:00:02:02 + eth2 UP 02:00:00:00:02:03 + eth5 DOWN 02:00:00:00:02:06 + + +5. Show Neighbor Entries for a VRF + + To list neighbor entries associated with devices enslaved to a VRF device + add the master option to the ip command: + $ ip [-6] neigh show master VRF-NAME + + For example: + $ ip neigh show master vrf-red + 10.2.1.254 dev eth1 lladdr a6:d9:c7:4f:06:23 REACHABLE + 10.2.2.254 dev eth2 lladdr 5e:54:01:6a:ee:80 REACHABLE + + $ ip -6 neigh show master vrf-red + 2002:1::64 dev eth1 lladdr a6:d9:c7:4f:06:23 REACHABLE + + +6. Show Addresses for a VRF + + To show addresses for interfaces associated with a VRF add the master + option to the ip command: + $ ip addr show master VRF-NAME + + For example: + $ ip addr show master vrf-red + 3: eth1: mtu 1500 qdisc pfifo_fast master vrf-red state UP group default qlen 1000 + link/ether 02:00:00:00:02:02 brd ff:ff:ff:ff:ff:ff + inet 10.2.1.2/24 brd 10.2.1.255 scope global eth1 + valid_lft forever preferred_lft forever + inet6 2002:1::2/120 scope global + valid_lft forever preferred_lft forever + inet6 fe80::ff:fe00:202/64 scope link + valid_lft forever preferred_lft forever + 4: eth2: mtu 1500 qdisc pfifo_fast master vrf-red state UP group default qlen 1000 + link/ether 02:00:00:00:02:03 brd ff:ff:ff:ff:ff:ff + inet 10.2.2.2/24 brd 10.2.2.255 scope global eth2 + valid_lft forever preferred_lft forever + inet6 2002:2::2/120 scope global + valid_lft forever preferred_lft forever + inet6 fe80::ff:fe00:203/64 scope link + valid_lft forever preferred_lft forever + 7: eth5: mtu 1500 qdisc noop master vrf-red state DOWN group default qlen 1000 + link/ether 02:00:00:00:02:06 brd ff:ff:ff:ff:ff:ff + + Or in brief format: + $ ip -br addr show master vrf-red + eth1 UP 10.2.1.2/24 2002:1::2/120 fe80::ff:fe00:202/64 + eth2 UP 10.2.2.2/24 2002:2::2/120 fe80::ff:fe00:203/64 + eth5 DOWN + + +7. Show Routes for a VRF + + To show routes for a VRF use the ip command to display the table associated + with the VRF device: + $ ip [-6] route show table ID + + For example: + $ ip route show table vrf-red + prohibit default + broadcast 10.2.1.0 dev eth1 proto kernel scope link src 10.2.1.2 + 10.2.1.0/24 dev eth1 proto kernel scope link src 10.2.1.2 + local 10.2.1.2 dev eth1 proto kernel scope host src 10.2.1.2 + broadcast 10.2.1.255 dev eth1 proto kernel scope link src 10.2.1.2 + broadcast 10.2.2.0 dev eth2 proto kernel scope link src 10.2.2.2 + 10.2.2.0/24 dev eth2 proto kernel scope link src 10.2.2.2 + local 10.2.2.2 dev eth2 proto kernel scope host src 10.2.2.2 + broadcast 10.2.2.255 dev eth2 proto kernel scope link src 10.2.2.2 + + $ ip -6 route show table vrf-red + local 2002:1:: dev lo proto none metric 0 pref medium + local 2002:1::2 dev lo proto none metric 0 pref medium + 2002:1::/120 dev eth1 proto kernel metric 256 pref medium + local 2002:2:: dev lo proto none metric 0 pref medium + local 2002:2::2 dev lo proto none metric 0 pref medium + 2002:2::/120 dev eth2 proto kernel metric 256 pref medium + local fe80:: dev lo proto none metric 0 pref medium + local fe80:: dev lo proto none metric 0 pref medium + local fe80::ff:fe00:202 dev lo proto none metric 0 pref medium + local fe80::ff:fe00:203 dev lo proto none metric 0 pref medium + fe80::/64 dev eth1 proto kernel metric 256 pref medium + fe80::/64 dev eth2 proto kernel metric 256 pref medium + ff00::/8 dev vrf-red metric 256 pref medium + ff00::/8 dev eth1 metric 256 pref medium + ff00::/8 dev eth2 metric 256 pref medium + + +8. Route Lookup for a VRF + + A test route lookup can be done for a VRF by adding the oif option to ip: + $ ip [-6] route get oif VRF-NAME ADDRESS + + For example: + $ ip route get 10.2.1.40 oif vrf-red + 10.2.1.40 dev eth1 table vrf-red src 10.2.1.2 + cache + + $ ip -6 route get 2002:1::32 oif vrf-red + 2002:1::32 from :: dev eth1 table vrf-red proto kernel src 2002:1::2 metric 256 pref medium + + +9. Removing Network Interface from a VRF + + Network interfaces are removed from a VRF by breaking the enslavement to + the VRF device: + $ ip link set dev NAME nomaster + + Connected routes are moved back to the default table and local entries are + moved to the local table. + + For example: + $ ip link set dev eth0 nomaster + +-------------------------------------------------------------------------------- + +Commands used in this example: + +cat >> /etc/iproute2/rt_tables < Date: Mon, 31 Aug 2015 19:54:41 -0400 Subject: i40e/i40evf: Add new link status defines Add the new Port link status bit and rename the link status to function link status. Change-ID: I71289327ae62638ce967b6ad40114caf998b6dab Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h index 785b3db..02d5225 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h @@ -1722,11 +1722,13 @@ struct i40e_aqc_get_link_status { u8 phy_type; /* i40e_aq_phy_type */ u8 link_speed; /* i40e_aq_link_speed */ u8 link_info; -#define I40E_AQ_LINK_UP 0x01 +#define I40E_AQ_LINK_UP 0x01 /* obsolete */ +#define I40E_AQ_LINK_UP_FUNCTION 0x01 #define I40E_AQ_LINK_FAULT 0x02 #define I40E_AQ_LINK_FAULT_TX 0x04 #define I40E_AQ_LINK_FAULT_RX 0x08 #define I40E_AQ_LINK_FAULT_REMOTE 0x10 +#define I40E_AQ_LINK_UP_PORT 0x20 #define I40E_AQ_MEDIA_AVAILABLE 0x40 #define I40E_AQ_SIGNAL_DETECT 0x80 u8 an_info; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h index c802209..fcb9ef3 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h @@ -1719,11 +1719,13 @@ struct i40e_aqc_get_link_status { u8 phy_type; /* i40e_aq_phy_type */ u8 link_speed; /* i40e_aq_link_speed */ u8 link_info; -#define I40E_AQ_LINK_UP 0x01 +#define I40E_AQ_LINK_UP 0x01 /* obsolete */ +#define I40E_AQ_LINK_UP_FUNCTION 0x01 #define I40E_AQ_LINK_FAULT 0x02 #define I40E_AQ_LINK_FAULT_TX 0x04 #define I40E_AQ_LINK_FAULT_RX 0x08 #define I40E_AQ_LINK_FAULT_REMOTE 0x10 +#define I40E_AQ_LINK_UP_PORT 0x20 #define I40E_AQ_MEDIA_AVAILABLE 0x40 #define I40E_AQ_SIGNAL_DETECT 0x80 u8 an_info; -- cgit v0.10.2 From 9f4ffc4426c4a4b6d4a84413e13efba6a72bf81d Mon Sep 17 00:00:00 2001 From: Carolyn Wyborny Date: Mon, 31 Aug 2015 19:54:42 -0400 Subject: i40e: Make it clear a parameter is never used Flag the filter_mask parameter as __always_unused in the ndo_bridge_getlink function. Change-ID: Ifc1e99c7fb84bcbf81cf7b0ac891ad8ca956ffb2 Signed-off-by: Carolyn Wyborny Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index a484f22..d5d8b66 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -8336,7 +8336,8 @@ static int i40e_ndo_bridge_setlink(struct net_device *dev, **/ static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, struct net_device *dev, - u32 filter_mask, int nlflags) + u32 __always_unused filter_mask, + int nlflags) { struct i40e_netdev_priv *np = netdev_priv(dev); struct i40e_vsi *vsi = np->vsi; -- cgit v0.10.2 From ab252253e08c16de28ec0f6da0a7ff684a8fce98 Mon Sep 17 00:00:00 2001 From: Neerav Parikh Date: Mon, 31 Aug 2015 19:54:43 -0400 Subject: i40e: Use BIT() macro for priority map parsing Replace one left over (1 << up) in the i40e_dcb.c file with the BIT() macro. Change-ID: I39492a400a2cee5ac566143a5b436cc478bea0db Signed-off-by: Neerav Parikh Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb.c b/drivers/net/ethernet/intel/i40e/i40e_dcb.c index 6fa07ef..251a841 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb.c +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb.c @@ -393,7 +393,7 @@ static void i40e_parse_cee_app_tlv(struct i40e_cee_feat_tlv *tlv, for (i = 0; i < dcbcfg->numapps; i++) { app = (struct i40e_cee_app_prio *)(tlv->tlvinfo + offset); for (up = 0; up < I40E_MAX_USER_PRIORITY; up++) { - if (app->prio_map & (1 << up)) + if (app->prio_map & BIT(up)) break; } dcbcfg->app[i].priority = up; -- cgit v0.10.2 From 14e52ee26bdfd6ad00075b66089b4b63fe36fff0 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Mon, 31 Aug 2015 19:54:44 -0400 Subject: i40evf: properly handle ndo_set_mac_address calls The driver was not correctly handling calls to its ndo_set_mac_address method. It did not properly check to see if the override would be allowed by the PF driver, and never removed the old address from its filter list. Add a new flag to the adapter struct which is set if the MAC address is assigned by the PF. Check this flag and don't allow the MAC address to be changed if it is set. Search for and properly remove the filter for the old MAC address when the new one is set. Change-ID: I817bf620c869c5a80e6a7eab65c9cbad1dc89799 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index e7a223e..6f42711 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -220,6 +220,7 @@ struct i40evf_adapter { #define I40EVF_FLAG_RESET_NEEDED BIT(10) #define I40EVF_FLAG_WB_ON_ITR_CAPABLE BIT(11) #define I40EVF_FLAG_OUTER_UDP_CSUM_CAPABLE BIT(12) +#define I40EVF_FLAG_ADDR_SET_BY_PF BIT(13) /* duplicates for common code */ #define I40E_FLAG_FDIR_ATR_ENABLED 0 #define I40E_FLAG_DCB_ENABLED 0 diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index c00e495..7ea7d2e 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -841,6 +841,15 @@ static int i40evf_set_mac(struct net_device *netdev, void *p) if (ether_addr_equal(netdev->dev_addr, addr->sa_data)) return 0; + if (adapter->flags & I40EVF_FLAG_ADDR_SET_BY_PF) + return -EPERM; + + f = i40evf_find_filter(adapter, hw->mac.addr); + if (f) { + f->remove = true; + adapter->aq_required |= I40EVF_FLAG_AQ_DEL_MAC_FILTER; + } + f = i40evf_add_filter(adapter, addr->sa_data); if (f) { ether_addr_copy(hw->mac.addr, addr->sa_data); @@ -2244,10 +2253,13 @@ static void i40evf_init_task(struct work_struct *work) if (!is_valid_ether_addr(adapter->hw.mac.addr)) { dev_info(&pdev->dev, "Invalid MAC address %pM, using random\n", adapter->hw.mac.addr); - random_ether_addr(adapter->hw.mac.addr); + eth_hw_addr_random(netdev); + ether_addr_copy(adapter->hw.mac.addr, netdev->dev_addr); + } else { + adapter->flags |= I40EVF_FLAG_ADDR_SET_BY_PF; + ether_addr_copy(netdev->dev_addr, adapter->hw.mac.addr); + ether_addr_copy(netdev->perm_addr, adapter->hw.mac.addr); } - ether_addr_copy(netdev->dev_addr, adapter->hw.mac.addr); - ether_addr_copy(netdev->perm_addr, adapter->hw.mac.addr); init_timer(&adapter->watchdog_timer); adapter->watchdog_timer.function = &i40evf_watchdog_timer; -- cgit v0.10.2 From ac24382d41cc72541d5f8810336acf98a5e6d260 Mon Sep 17 00:00:00 2001 From: Carolyn Wyborny Date: Mon, 31 Aug 2015 19:54:45 -0400 Subject: i40e/i40evf: Add info to nvm info struct for OEM version data This patch adds a member to the nvm_info struct for oem_ver info to be output either by OID or ethtool. Change-ID: I1e5d513ae67622e2af17042924fdb4b5d6d85366 Signed-off-by: Carolyn Wyborny Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.c b/drivers/net/ethernet/intel/i40e/i40e_adminq.c index fa2e916..5c950e2 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq.c +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.c @@ -553,8 +553,9 @@ shutdown_arq_out: **/ i40e_status i40e_init_adminq(struct i40e_hw *hw) { - i40e_status ret_code; + u16 cfg_ptr, oem_hi, oem_lo; u16 eetrack_lo, eetrack_hi; + i40e_status ret_code; int retry = 0; /* verify input for valid configuration */ @@ -613,6 +614,12 @@ i40e_status i40e_init_adminq(struct i40e_hw *hw) i40e_read_nvm_word(hw, I40E_SR_NVM_EETRACK_LO, &eetrack_lo); i40e_read_nvm_word(hw, I40E_SR_NVM_EETRACK_HI, &eetrack_hi); hw->nvm.eetrack = (eetrack_hi << 16) | eetrack_lo; + i40e_read_nvm_word(hw, I40E_SR_BOOT_CONFIG_PTR, &cfg_ptr); + i40e_read_nvm_word(hw, (cfg_ptr + I40E_NVM_OEM_VER_OFF), + &oem_hi); + i40e_read_nvm_word(hw, (cfg_ptr + (I40E_NVM_OEM_VER_OFF + 1)), + &oem_lo); + hw->nvm.oem_ver = ((u32)oem_hi << 16) | oem_lo; if (hw->aq.api_maj_ver > I40E_FW_API_VERSION_MAJOR) { ret_code = I40E_ERR_FIRMWARE_API_VERSION; diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index d1ec5a4..c8f7a52 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -289,6 +289,7 @@ struct i40e_nvm_info { bool blank_nvm_mode; /* is NVM empty (no FW present)*/ u16 version; /* NVM package version */ u32 eetrack; /* NVM data version */ + u32 oem_ver; /* OEM version info */ }; /* definitions used in NVM update support */ @@ -1204,6 +1205,8 @@ struct i40e_hw_port_stats { #define I40E_SR_EMP_MODULE_PTR 0x0F #define I40E_SR_PBA_FLAGS 0x15 #define I40E_SR_PBA_BLOCK_PTR 0x16 +#define I40E_SR_BOOT_CONFIG_PTR 0x17 +#define I40E_NVM_OEM_VER_OFF 0x83 #define I40E_SR_NVM_DEV_STARTER_VERSION 0x18 #define I40E_SR_NVM_WAKE_ON_LAN 0x19 #define I40E_SR_ALTERNATE_SAN_MAC_ADDRESS_PTR 0x27 diff --git a/drivers/net/ethernet/intel/i40evf/i40e_type.h b/drivers/net/ethernet/intel/i40evf/i40e_type.h index a59b60f..b3c65dd 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_type.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_type.h @@ -288,6 +288,7 @@ struct i40e_nvm_info { bool blank_nvm_mode; /* is NVM empty (no FW present)*/ u16 version; /* NVM package version */ u32 eetrack; /* NVM data version */ + u32 oem_ver; /* OEM version info */ }; /* definitions used in NVM update support */ @@ -1173,6 +1174,7 @@ struct i40e_hw_port_stats { /* Checksum and Shadow RAM pointers */ #define I40E_SR_NVM_CONTROL_WORD 0x00 #define I40E_SR_EMP_MODULE_PTR 0x0F +#define I40E_NVM_OEM_VER_OFF 0x83 #define I40E_SR_NVM_DEV_STARTER_VERSION 0x18 #define I40E_SR_NVM_WAKE_ON_LAN 0x19 #define I40E_SR_ALTERNATE_SAN_MAC_ADDRESS_PTR 0x27 -- cgit v0.10.2 From 41c3ae8b726e07e366fdb63e0e40a33528f1c3e4 Mon Sep 17 00:00:00 2001 From: Greg Bowers Date: Mon, 31 Aug 2015 19:54:46 -0400 Subject: i40e: Convert CEE App TLV selector to IEEE selector Changes the parsing of CEE App TLVs to fill in the App selector in struct i40e_dcbx_config with the IEEE App selector so the caller doesn't have to consider whether the App came from a CEE or IEEE DCBX negotiation. Change-ID: Ia7d9d664cde04d2ebcc9822fd22e4929c6edab3a Signed-off-by: Greg Bowers Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb.c b/drivers/net/ethernet/intel/i40e/i40e_dcb.c index 251a841..2691277 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb.c +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb.c @@ -380,7 +380,7 @@ static void i40e_parse_cee_app_tlv(struct i40e_cee_feat_tlv *tlv, { u16 length, typelength, offset = 0; struct i40e_cee_app_prio *app; - u8 i, up; + u8 i, up, selector; typelength = ntohs(tlv->hdr.typelen); length = (u16)((typelength & I40E_LLDP_TLV_LEN_MASK) >> @@ -397,9 +397,17 @@ static void i40e_parse_cee_app_tlv(struct i40e_cee_feat_tlv *tlv, break; } dcbcfg->app[i].priority = up; - /* Get Selector from lower 2 bits */ - dcbcfg->app[i].selector = (app->upper_oui_sel & - I40E_CEE_APP_SELECTOR_MASK); + + /* Get Selector from lower 2 bits, and convert to IEEE */ + selector = (app->upper_oui_sel & I40E_CEE_APP_SELECTOR_MASK); + if (selector == I40E_CEE_APP_SEL_ETHTYPE) + dcbcfg->app[i].selector = I40E_APP_SEL_ETHTYPE; + else if (selector == I40E_CEE_APP_SEL_TCPIP) + dcbcfg->app[i].selector = I40E_APP_SEL_TCPIP; + else + /* Keep selector as it is for unknown types */ + dcbcfg->app[i].selector = selector; + dcbcfg->app[i].protocolid = ntohs(app->protocol); /* Move to next app */ offset += sizeof(*app); diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index c8f7a52..4ec3ffa 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -418,6 +418,8 @@ struct i40e_fc_info { #define I40E_APP_PROTOID_FIP 0x8914 #define I40E_APP_SEL_ETHTYPE 0x1 #define I40E_APP_SEL_TCPIP 0x2 +#define I40E_CEE_APP_SEL_ETHTYPE 0x0 +#define I40E_CEE_APP_SEL_TCPIP 0x1 /* CEE or IEEE 802.1Qaz ETS Configuration data */ struct i40e_dcb_ets_config { -- cgit v0.10.2 From e7e6cfce16e9184497716e004fc9e714a7411ad0 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Mon, 31 Aug 2015 19:54:47 -0400 Subject: i40e: remove redundant call This function call isn't needed here; the same function is already called by i40e_reset_vf. Change-ID: I96ccbf91b752965c9e28fe895d4c7d4c46e3ba44 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index ee747dc..2102280 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -964,8 +964,6 @@ int i40e_alloc_vfs(struct i40e_pf *pf, u16 num_alloc_vfs) /* VF resources get allocated during reset */ i40e_reset_vf(&vfs[i], false); - /* enable VF vplan_qtable mappings */ - i40e_enable_vf_mappings(&vfs[i]); } pf->num_alloc_vfs = num_alloc_vfs; -- cgit v0.10.2 From 21be99ec4ed366e24d4735d230aa3086ff2bc6ed Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Mon, 31 Aug 2015 19:54:48 -0400 Subject: i40e: don't panic on VSI allocation failure In some circumstances, the firmware may fail to allocate a VSI for a VF. When this happens, the driver does not react well to the bad news and has a panic attack. To fix this problem, check the return value from i40e_alloc_vf_res and don't try to configure the device further if it failed. Additionally, explicitly clear the INIT bit when we free VF resources, so that this bit will be in the proper state in the failure case, and won't blow up elsewhere. Change-ID: I6a20ce2b59c3458fd832032e88fa28cd42500189 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 2102280..ae7548a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -703,6 +703,7 @@ static void i40e_free_vf_res(struct i40e_vf *vf) */ vf->num_queue_pairs = 0; vf->vf_states = 0; + clear_bit(I40E_VF_STAT_INIT, &vf->vf_states); } /** @@ -841,11 +842,11 @@ void i40e_reset_vf(struct i40e_vf *vf, bool flr) complete_reset: /* reallocate VF resources to reset the VSI state */ i40e_free_vf_res(vf); - i40e_alloc_vf_res(vf); - i40e_enable_vf_mappings(vf); - set_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states); - clear_bit(I40E_VF_STAT_DISABLED, &vf->vf_states); - + if (!i40e_alloc_vf_res(vf)) { + i40e_enable_vf_mappings(vf); + set_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states); + clear_bit(I40E_VF_STAT_DISABLED, &vf->vf_states); + } /* tell the VF the reset is done */ wr32(hw, I40E_VFGEN_RSTAT1(vf->vf_id), I40E_VFR_VFACTIVE); i40e_flush(hw); -- cgit v0.10.2 From f0b44440148c257bb5b1872e8ff5d6591afc8f4f Mon Sep 17 00:00:00 2001 From: Carolyn Wyborny Date: Mon, 31 Aug 2015 19:54:49 -0400 Subject: i40e: update fw version text string per previous product formats This patch moves the internal fw version and fw api version info to be output in probe. The nvm version, etrack and oem version info are now configured for output via ethtool -i. Change-ID: I05d490093a7137dbefcdef263d014d1e5c9e83d0 Signed-off-by: Carolyn Wyborny Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index f26dcb2..cfe8f83 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -107,6 +107,8 @@ #define I40E_NVM_VERSION_LO_MASK (0xff << I40E_NVM_VERSION_LO_SHIFT) #define I40E_NVM_VERSION_HI_SHIFT 12 #define I40E_NVM_VERSION_HI_MASK (0xf << I40E_NVM_VERSION_HI_SHIFT) +#define I40E_OEM_VER_BUILD_MASK 0xff00 +#define I40E_OEM_VER_PATCH_MASK 0xff /* The values in here are decimal coded as hex as is the case in the NVM map*/ #define I40E_CURRENT_NVM_VERSION_HI 0x2 @@ -587,14 +589,14 @@ static inline char *i40e_fw_version_str(struct i40e_hw *hw) static char buf[32]; snprintf(buf, sizeof(buf), - "f%d.%d.%05d a%d.%d n%x.%02x e%x", - hw->aq.fw_maj_ver, hw->aq.fw_min_ver, hw->aq.fw_build, - hw->aq.api_maj_ver, hw->aq.api_min_ver, + "%x.%02x 0x%x %d.%d.%d", (hw->nvm.version & I40E_NVM_VERSION_HI_MASK) >> I40E_NVM_VERSION_HI_SHIFT, (hw->nvm.version & I40E_NVM_VERSION_LO_MASK) >> I40E_NVM_VERSION_LO_SHIFT, - (hw->nvm.eetrack & 0xffffff)); + hw->nvm.eetrack, (hw->nvm.oem_ver >> 24), + (hw->nvm.oem_ver & I40E_OEM_VER_BUILD_MASK) >> 8, + hw->nvm.oem_ver & I40E_OEM_VER_PATCH_MASK); return buf; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index d5d8b66..45b3292 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -10064,6 +10064,13 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err = i40e_init_adminq(hw); dev_info(&pdev->dev, "%s\n", i40e_fw_version_str(hw)); + + /* provide additional fw info, like api and ver */ + dev_info(&pdev->dev, "fw_version:%d.%d.%05d\n", + hw->aq.fw_maj_ver, hw->aq.fw_min_ver, hw->aq.fw_build); + dev_info(&pdev->dev, "fw api version:%d.%d\n", + hw->aq.api_maj_ver, hw->aq.api_min_ver); + if (err) { dev_info(&pdev->dev, "The driver for the device stopped because the NVM image is newer than expected. You must install the most recent version of the network driver.\n"); -- cgit v0.10.2 From d72c95ea422ab63c832a13c075cb73bc87a1276e Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Mon, 31 Aug 2015 19:54:50 -0400 Subject: i40e/i40evf: split device ids into a separate file Due to desires to write userland drivers, and other requests, without needing the rest of the include files, the device ids are pulled out into a standalone file. Change-ID: Ic0b047dbf9d4b0891892309c1f2079f56d9b60e8 Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_devids.h b/drivers/net/ethernet/intel/i40e/i40e_devids.h new file mode 100644 index 0000000..c601ca4 --- /dev/null +++ b/drivers/net/ethernet/intel/i40e/i40e_devids.h @@ -0,0 +1,55 @@ +/******************************************************************************* + * + * Intel Ethernet Controller XL710 Family Linux Driver + * Copyright(c) 2013 - 2015 Intel Corporation. + * + * 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". + * + * Contact Information: + * e1000-devel Mailing List + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + ******************************************************************************/ + +#ifndef _I40E_DEVIDS_H_ +#define _I40E_DEVIDS_H_ + +/* Device IDs */ +#define I40E_DEV_ID_SFP_XL710 0x1572 +#define I40E_DEV_ID_QEMU 0x1574 +#define I40E_DEV_ID_KX_A 0x157F +#define I40E_DEV_ID_KX_B 0x1580 +#define I40E_DEV_ID_KX_C 0x1581 +#define I40E_DEV_ID_QSFP_A 0x1583 +#define I40E_DEV_ID_QSFP_B 0x1584 +#define I40E_DEV_ID_QSFP_C 0x1585 +#define I40E_DEV_ID_10G_BASE_T 0x1586 +#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_VF 0x154C +#define I40E_DEV_ID_VF_HV 0x1571 +#define I40E_DEV_ID_SFP_X722 0x37D0 +#define I40E_DEV_ID_1G_BASE_T_X722 0x37D1 +#define I40E_DEV_ID_10G_BASE_T_X722 0x37D2 +#define I40E_DEV_ID_X722_VF 0x37CD +#define I40E_DEV_ID_X722_VF_HV 0x37D9 + +#define i40e_is_40G_device(d) ((d) == I40E_DEV_ID_QSFP_A || \ + (d) == I40E_DEV_ID_QSFP_B || \ + (d) == I40E_DEV_ID_QSFP_C) + +#endif /* _I40E_DEVIDS_H_ */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index 4ec3ffa..102d0c4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -33,31 +33,7 @@ #include "i40e_adminq.h" #include "i40e_hmc.h" #include "i40e_lan_hmc.h" - -/* Device IDs */ -#define I40E_DEV_ID_SFP_XL710 0x1572 -#define I40E_DEV_ID_QEMU 0x1574 -#define I40E_DEV_ID_KX_A 0x157F -#define I40E_DEV_ID_KX_B 0x1580 -#define I40E_DEV_ID_KX_C 0x1581 -#define I40E_DEV_ID_QSFP_A 0x1583 -#define I40E_DEV_ID_QSFP_B 0x1584 -#define I40E_DEV_ID_QSFP_C 0x1585 -#define I40E_DEV_ID_10G_BASE_T 0x1586 -#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_VF 0x154C -#define I40E_DEV_ID_VF_HV 0x1571 -#define I40E_DEV_ID_SFP_X722 0x37D0 -#define I40E_DEV_ID_1G_BASE_T_X722 0x37D1 -#define I40E_DEV_ID_10G_BASE_T_X722 0x37D2 -#define I40E_DEV_ID_X722_VF 0x37CD -#define I40E_DEV_ID_X722_VF_HV 0x37D9 - -#define i40e_is_40G_device(d) ((d) == I40E_DEV_ID_QSFP_A || \ - (d) == I40E_DEV_ID_QSFP_B || \ - (d) == I40E_DEV_ID_QSFP_C) +#include "i40e_devids.h" /* I40E_MASK is a macro used on 32 bit registers */ #define I40E_MASK(mask, shift) (mask << shift) diff --git a/drivers/net/ethernet/intel/i40evf/i40e_devids.h b/drivers/net/ethernet/intel/i40evf/i40e_devids.h new file mode 100644 index 0000000..e6a39c9 --- /dev/null +++ b/drivers/net/ethernet/intel/i40evf/i40e_devids.h @@ -0,0 +1,55 @@ +/******************************************************************************* + * + * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver + * Copyright(c) 2013 - 2015 Intel Corporation. + * + * 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". + * + * Contact Information: + * e1000-devel Mailing List + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + ******************************************************************************/ + +#ifndef _I40E_DEVIDS_H_ +#define _I40E_DEVIDS_H_ + +/* Device IDs */ +#define I40E_DEV_ID_SFP_XL710 0x1572 +#define I40E_DEV_ID_QEMU 0x1574 +#define I40E_DEV_ID_KX_A 0x157F +#define I40E_DEV_ID_KX_B 0x1580 +#define I40E_DEV_ID_KX_C 0x1581 +#define I40E_DEV_ID_QSFP_A 0x1583 +#define I40E_DEV_ID_QSFP_B 0x1584 +#define I40E_DEV_ID_QSFP_C 0x1585 +#define I40E_DEV_ID_10G_BASE_T 0x1586 +#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_VF 0x154C +#define I40E_DEV_ID_VF_HV 0x1571 +#define I40E_DEV_ID_SFP_X722 0x37D0 +#define I40E_DEV_ID_1G_BASE_T_X722 0x37D1 +#define I40E_DEV_ID_10G_BASE_T_X722 0x37D2 +#define I40E_DEV_ID_X722_VF 0x37CD +#define I40E_DEV_ID_X722_VF_HV 0x37D9 + +#define i40e_is_40G_device(d) ((d) == I40E_DEV_ID_QSFP_A || \ + (d) == I40E_DEV_ID_QSFP_B || \ + (d) == I40E_DEV_ID_QSFP_C) + +#endif /* _I40E_DEVIDS_H_ */ diff --git a/drivers/net/ethernet/intel/i40evf/i40e_type.h b/drivers/net/ethernet/intel/i40evf/i40e_type.h index b3c65dd..6dbace3 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_type.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_type.h @@ -33,31 +33,7 @@ #include "i40e_adminq.h" #include "i40e_hmc.h" #include "i40e_lan_hmc.h" - -/* Device IDs */ -#define I40E_DEV_ID_SFP_XL710 0x1572 -#define I40E_DEV_ID_QEMU 0x1574 -#define I40E_DEV_ID_KX_A 0x157F -#define I40E_DEV_ID_KX_B 0x1580 -#define I40E_DEV_ID_KX_C 0x1581 -#define I40E_DEV_ID_QSFP_A 0x1583 -#define I40E_DEV_ID_QSFP_B 0x1584 -#define I40E_DEV_ID_QSFP_C 0x1585 -#define I40E_DEV_ID_10G_BASE_T 0x1586 -#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_VF 0x154C -#define I40E_DEV_ID_VF_HV 0x1571 -#define I40E_DEV_ID_SFP_X722 0x37D0 -#define I40E_DEV_ID_1G_BASE_T_X722 0x37D1 -#define I40E_DEV_ID_10G_BASE_T_X722 0x37D2 -#define I40E_DEV_ID_X722_VF 0x37CD -#define I40E_DEV_ID_X722_VF_HV 0x37D9 - -#define i40e_is_40G_device(d) ((d) == I40E_DEV_ID_QSFP_A || \ - (d) == I40E_DEV_ID_QSFP_B || \ - (d) == I40E_DEV_ID_QSFP_C) +#include "i40e_devids.h" /* I40E_MASK is a macro used on 32 bit registers */ #define I40E_MASK(mask, shift) (mask << shift) -- cgit v0.10.2 From 0a862b43acc6c5d38fd462baa9c76f9197907d73 Mon Sep 17 00:00:00 2001 From: Catherine Sullivan Date: Mon, 31 Aug 2015 19:54:53 -0400 Subject: i40e/i40evf: Add module_types and update_link_info Add a module_types variable to the link_info struct to save the module information from get_phy_capabilities. This information can be used to determine which speeds the module supports. Also add a new function update_link_info which updates the module_types parameter and then calls get_link_info. This function should be called in place of get_link_info so that the module_types variable stays up-to-date with the rest of the link information. The EAS table does not reflect the values that are actually returned, so instead, basing these values on the Ethernet compliance codes specified in table 33 of SFF-8436 as these have been accurate. Use the new variable in ethtool to differentiate between a 10G/1G dual speed fiber module and a 10G only module. Change-ID: Ib7585cce321319c10ce15180054c41a6cbd41389 Signed-off-by: Catherine Sullivan Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 2d012d9..ffd1cd0 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -1717,14 +1717,14 @@ enum i40e_status_code i40e_set_fc(struct i40e_hw *hw, u8 *aq_failures, *aq_failures |= I40E_SET_FC_AQ_FAIL_SET; } /* Update the link info */ - status = i40e_aq_get_link_info(hw, true, NULL, NULL); + status = i40e_update_link_info(hw); if (status) { /* Wait a little bit (on 40G cards it sometimes takes a really * long time for link to come back from the atomic reset) * and try once more */ msleep(1000); - status = i40e_aq_get_link_info(hw, true, NULL, NULL); + status = i40e_update_link_info(hw); } if (status) *aq_failures |= I40E_SET_FC_AQ_FAIL_UPDATE; @@ -2247,7 +2247,7 @@ i40e_status i40e_get_link_status(struct i40e_hw *hw, bool *link_up) i40e_status status = 0; if (hw->phy.get_link_info) { - status = i40e_aq_get_link_info(hw, true, NULL, NULL); + status = i40e_update_link_info(hw); if (status) i40e_debug(hw, I40E_DEBUG_LINK, "get link failed: status %d\n", @@ -2260,6 +2260,30 @@ i40e_status i40e_get_link_status(struct i40e_hw *hw, bool *link_up) } /** + * i40e_updatelink_status - update status of the HW network link + * @hw: pointer to the hw struct + **/ +i40e_status i40e_update_link_info(struct i40e_hw *hw) +{ + struct i40e_aq_get_phy_abilities_resp abilities; + i40e_status status = 0; + + status = i40e_aq_get_link_info(hw, true, NULL, NULL); + if (status) + return status; + + status = i40e_aq_get_phy_capabilities(hw, false, false, &abilities, + NULL); + if (status) + return status; + + memcpy(hw->phy.link_info.module_type, &abilities.module_type, + sizeof(hw->phy.link_info.module_type)); + + return status; +} + +/** * i40e_aq_add_veb - Insert a VEB between the VSI and the MAC * @hw: pointer to the hw struct * @uplink_seid: the MAC or other gizmo SEID diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 148f614..46019e9 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -307,12 +307,18 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw, case I40E_PHY_TYPE_10GBASE_LR: case I40E_PHY_TYPE_1000BASE_SX: case I40E_PHY_TYPE_1000BASE_LX: - ecmd->supported = SUPPORTED_10000baseT_Full | - SUPPORTED_1000baseT_Full; + ecmd->supported = SUPPORTED_10000baseT_Full; + if (hw_link_info->module_type[2] & + I40E_MODULE_TYPE_1000BASE_SX || + hw_link_info->module_type[2] & + I40E_MODULE_TYPE_1000BASE_LX) { + ecmd->supported |= SUPPORTED_1000baseT_Full; + if (hw_link_info->requested_speeds & + I40E_LINK_SPEED_1GB) + ecmd->advertising |= ADVERTISED_1000baseT_Full; + } if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) ecmd->advertising |= ADVERTISED_10000baseT_Full; - if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) - ecmd->advertising |= ADVERTISED_1000baseT_Full; break; case I40E_PHY_TYPE_1000BASE_KX: ecmd->supported = SUPPORTED_Autoneg | @@ -704,7 +710,7 @@ static int i40e_set_settings(struct net_device *netdev, return -EAGAIN; } - status = i40e_aq_get_link_info(hw, true, NULL, NULL); + status = i40e_update_link_info(hw); if (status) netdev_info(netdev, "Updating link info failed with err %s aq_err %s\n", i40e_stat_str(hw, status), diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 45b3292..dc80a5f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -9728,7 +9728,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit) i40e_config_rss(pf); /* fill in link information and enable LSE reporting */ - i40e_aq_get_link_info(&pf->hw, true, NULL, NULL); + i40e_update_link_info(&pf->hw); i40e_link_event(pf); /* Initialize user-specific link properties */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h index e51e156..4454974 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h +++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h @@ -259,6 +259,7 @@ i40e_status i40e_pf_reset(struct i40e_hw *hw); void i40e_clear_hw(struct i40e_hw *hw); void i40e_clear_pxe_mode(struct i40e_hw *hw); i40e_status i40e_get_link_status(struct i40e_hw *hw, bool *link_up); +i40e_status i40e_update_link_info(struct i40e_hw *hw); i40e_status i40e_get_mac_addr(struct i40e_hw *hw, u8 *mac_addr); i40e_status i40e_read_bw_from_alt_ram(struct i40e_hw *hw, u32 *max_bw, u32 *min_bw, bool *min_valid, diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index 102d0c4..0a450ac 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -167,6 +167,24 @@ struct i40e_link_status { bool crc_enable; u8 pacing; u8 requested_speeds; + u8 module_type[3]; + /* 1st byte: module identifier */ +#define I40E_MODULE_TYPE_SFP 0x03 +#define I40E_MODULE_TYPE_QSFP 0x0D + /* 2nd byte: ethernet compliance codes for 10/40G */ +#define I40E_MODULE_TYPE_40G_ACTIVE 0x01 +#define I40E_MODULE_TYPE_40G_LR4 0x02 +#define I40E_MODULE_TYPE_40G_SR4 0x04 +#define I40E_MODULE_TYPE_40G_CR4 0x08 +#define I40E_MODULE_TYPE_10G_BASE_SR 0x10 +#define I40E_MODULE_TYPE_10G_BASE_LR 0x20 +#define I40E_MODULE_TYPE_10G_BASE_LRM 0x40 +#define I40E_MODULE_TYPE_10G_BASE_ER 0x80 + /* 3rd byte: ethernet compliance codes for 1G */ +#define I40E_MODULE_TYPE_1000BASE_SX 0x01 +#define I40E_MODULE_TYPE_1000BASE_LX 0x02 +#define I40E_MODULE_TYPE_1000BASE_CX 0x04 +#define I40E_MODULE_TYPE_1000BASE_T 0x08 }; struct i40e_phy_info { diff --git a/drivers/net/ethernet/intel/i40evf/i40e_type.h b/drivers/net/ethernet/intel/i40evf/i40e_type.h index 6dbace3..f3155e3 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_type.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_type.h @@ -167,6 +167,24 @@ struct i40e_link_status { bool crc_enable; u8 pacing; u8 requested_speeds; + u8 module_type[3]; + /* 1st byte: module identifier */ +#define I40E_MODULE_TYPE_SFP 0x03 +#define I40E_MODULE_TYPE_QSFP 0x0D + /* 2nd byte: ethernet compliance codes for 10/40G */ +#define I40E_MODULE_TYPE_40G_ACTIVE 0x01 +#define I40E_MODULE_TYPE_40G_LR4 0x02 +#define I40E_MODULE_TYPE_40G_SR4 0x04 +#define I40E_MODULE_TYPE_40G_CR4 0x08 +#define I40E_MODULE_TYPE_10G_BASE_SR 0x10 +#define I40E_MODULE_TYPE_10G_BASE_LR 0x20 +#define I40E_MODULE_TYPE_10G_BASE_LRM 0x40 +#define I40E_MODULE_TYPE_10G_BASE_ER 0x80 + /* 3rd byte: ethernet compliance codes for 1G */ +#define I40E_MODULE_TYPE_1000BASE_SX 0x01 +#define I40E_MODULE_TYPE_1000BASE_LX 0x02 +#define I40E_MODULE_TYPE_1000BASE_CX 0x04 +#define I40E_MODULE_TYPE_1000BASE_T 0x08 }; struct i40e_phy_info { -- cgit v0.10.2 From fc72dbce09989b268b20342473ddc8d77c8da350 Mon Sep 17 00:00:00 2001 From: Catherine Sullivan Date: Tue, 1 Sep 2015 11:36:30 -0400 Subject: i40e/i40evf: Refactor PHY structure and add phy_capabilities enum Remove unused members in the PHY structure and add a new member to store all the capabilities the PHY has as reported by the FW. This information will help us determine what speeds the device is capable of when link is down. Also add an enum to decode the PHY types the NVM is capable of. Use the phy_types variable to determine what phy types are possible when link is down instead of device id as it will be more accurate. When on a backplane device, we do not support changing any settings, however we should display all the phy_types we are capable of so if we see a backplane dev ID set supported and advertised purely based on the phy_types variable. Change-ID: Ia75d560f1fcd30c54cbfb7458690c5867559a930 Signed-off-by: Catherine Sullivan Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 46019e9..831f971 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -273,36 +273,12 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw, case I40E_PHY_TYPE_40GBASE_AOC: ecmd->supported = SUPPORTED_40000baseCR4_Full; break; - case I40E_PHY_TYPE_40GBASE_KR4: - ecmd->supported = SUPPORTED_Autoneg | - SUPPORTED_40000baseKR4_Full; - ecmd->advertising = ADVERTISED_Autoneg | - ADVERTISED_40000baseKR4_Full; - break; case I40E_PHY_TYPE_40GBASE_SR4: ecmd->supported = SUPPORTED_40000baseSR4_Full; break; case I40E_PHY_TYPE_40GBASE_LR4: ecmd->supported = SUPPORTED_40000baseLR4_Full; break; - case I40E_PHY_TYPE_20GBASE_KR2: - ecmd->supported = SUPPORTED_Autoneg | - SUPPORTED_20000baseKR2_Full; - ecmd->advertising = ADVERTISED_Autoneg | - ADVERTISED_20000baseKR2_Full; - break; - case I40E_PHY_TYPE_10GBASE_KX4: - ecmd->supported = SUPPORTED_Autoneg | - SUPPORTED_10000baseKX4_Full; - ecmd->advertising = ADVERTISED_Autoneg | - ADVERTISED_10000baseKX4_Full; - break; - case I40E_PHY_TYPE_10GBASE_KR: - ecmd->supported = SUPPORTED_Autoneg | - SUPPORTED_10000baseKR_Full; - ecmd->advertising = ADVERTISED_Autoneg | - ADVERTISED_10000baseKR_Full; - break; case I40E_PHY_TYPE_10GBASE_SR: case I40E_PHY_TYPE_10GBASE_LR: case I40E_PHY_TYPE_1000BASE_SX: @@ -320,12 +296,6 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw, if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) ecmd->advertising |= ADVERTISED_10000baseT_Full; break; - case I40E_PHY_TYPE_1000BASE_KX: - ecmd->supported = SUPPORTED_Autoneg | - SUPPORTED_1000baseKX_Full; - ecmd->advertising = ADVERTISED_Autoneg | - ADVERTISED_1000baseKX_Full; - break; case I40E_PHY_TYPE_10GBASE_T: case I40E_PHY_TYPE_1000BASE_T: case I40E_PHY_TYPE_100BASE_TX: @@ -364,6 +334,15 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw, if (hw_link_info->requested_speeds & I40E_LINK_SPEED_100MB) ecmd->advertising |= ADVERTISED_100baseT_Full; break; + /* Backplane is set based on supported phy types in get_settings + * so don't set anything here but don't warn either + */ + case I40E_PHY_TYPE_40GBASE_KR4: + case I40E_PHY_TYPE_20GBASE_KR2: + case I40E_PHY_TYPE_10GBASE_KR: + case I40E_PHY_TYPE_10GBASE_KX4: + case I40E_PHY_TYPE_1000BASE_KX: + 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", @@ -403,64 +382,67 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw, static void i40e_get_settings_link_down(struct i40e_hw *hw, struct ethtool_cmd *ecmd) { - struct i40e_link_status *hw_link_info = &hw->phy.link_info; + enum i40e_aq_capabilities_phy_type phy_types = hw->phy.phy_types; /* link is down and the driver needs to fall back on - * device ID to determine what kinds of info to display, - * it's mostly a guess that may change when link is up + * supported phy types to figure out what info to display */ - switch (hw->device_id) { - case I40E_DEV_ID_QSFP_A: - case I40E_DEV_ID_QSFP_B: - case I40E_DEV_ID_QSFP_C: - /* pluggable QSFP */ - ecmd->supported = SUPPORTED_40000baseSR4_Full | - SUPPORTED_40000baseCR4_Full | - SUPPORTED_40000baseLR4_Full; - ecmd->advertising = ADVERTISED_40000baseSR4_Full | - ADVERTISED_40000baseCR4_Full | - ADVERTISED_40000baseLR4_Full; - break; - case I40E_DEV_ID_KX_B: - /* backplane 40G */ - ecmd->supported = SUPPORTED_40000baseKR4_Full; - ecmd->advertising = ADVERTISED_40000baseKR4_Full; - break; - case I40E_DEV_ID_KX_C: - /* backplane 10G */ - ecmd->supported = SUPPORTED_10000baseKR_Full; - ecmd->advertising = ADVERTISED_10000baseKR_Full; - break; - case I40E_DEV_ID_10G_BASE_T: - case I40E_DEV_ID_10G_BASE_T4: - ecmd->supported = SUPPORTED_10000baseT_Full | - SUPPORTED_1000baseT_Full | - SUPPORTED_100baseT_Full; - /* Figure out what has been requested */ - if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) - ecmd->advertising |= ADVERTISED_10000baseT_Full; - if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) - ecmd->advertising |= ADVERTISED_1000baseT_Full; - if (hw_link_info->requested_speeds & I40E_LINK_SPEED_100MB) - ecmd->advertising |= ADVERTISED_100baseT_Full; - break; - case I40E_DEV_ID_20G_KR2: - case I40E_DEV_ID_20G_KR2_A: - /* backplane 20G */ - ecmd->supported = SUPPORTED_20000baseKR2_Full; - ecmd->advertising = ADVERTISED_20000baseKR2_Full; - break; - default: - /* all the rest are 10G/1G */ - ecmd->supported = SUPPORTED_10000baseT_Full | - SUPPORTED_1000baseT_Full; - /* Figure out what has been requested */ - if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) - ecmd->advertising |= ADVERTISED_10000baseT_Full; - if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) - ecmd->advertising |= ADVERTISED_1000baseT_Full; - break; + ecmd->supported = 0x0; + ecmd->advertising = 0x0; + if (phy_types & I40E_CAP_PHY_TYPE_SGMII) { + ecmd->supported |= SUPPORTED_Autoneg | + SUPPORTED_1000baseT_Full | + SUPPORTED_100baseT_Full; + ecmd->advertising |= ADVERTISED_Autoneg | + ADVERTISED_1000baseT_Full | + ADVERTISED_100baseT_Full; + } + if (phy_types & I40E_CAP_PHY_TYPE_XAUI || + phy_types & I40E_CAP_PHY_TYPE_XFI || + phy_types & I40E_CAP_PHY_TYPE_SFI || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_SFPP_CU || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_AOC) + ecmd->supported |= SUPPORTED_10000baseT_Full; + if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_CR1_CU || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_CR1 || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_T || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_SR || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_LR) { + ecmd->supported |= SUPPORTED_Autoneg | + SUPPORTED_10000baseT_Full; + ecmd->advertising |= ADVERTISED_Autoneg | + ADVERTISED_10000baseT_Full; + } + if (phy_types & I40E_CAP_PHY_TYPE_XLAUI || + phy_types & I40E_CAP_PHY_TYPE_XLPPI || + phy_types & I40E_CAP_PHY_TYPE_40GBASE_AOC) + ecmd->supported |= SUPPORTED_40000baseCR4_Full; + if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_CR4_CU || + phy_types & I40E_CAP_PHY_TYPE_40GBASE_CR4) { + ecmd->supported |= SUPPORTED_Autoneg | + SUPPORTED_40000baseCR4_Full; + ecmd->advertising |= ADVERTISED_Autoneg | + ADVERTISED_40000baseCR4_Full; } + if (phy_types & I40E_CAP_PHY_TYPE_100BASE_TX) { + ecmd->supported |= SUPPORTED_Autoneg | + SUPPORTED_100baseT_Full; + ecmd->advertising |= ADVERTISED_Autoneg | + ADVERTISED_100baseT_Full; + } + if (phy_types & I40E_CAP_PHY_TYPE_1000BASE_T || + phy_types & I40E_CAP_PHY_TYPE_1000BASE_SX || + phy_types & I40E_CAP_PHY_TYPE_1000BASE_LX || + phy_types & I40E_CAP_PHY_TYPE_1000BASE_T_OPTICAL) { + ecmd->supported |= SUPPORTED_Autoneg | + SUPPORTED_1000baseT_Full; + ecmd->advertising |= ADVERTISED_Autoneg | + ADVERTISED_1000baseT_Full; + } + if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_SR4) + ecmd->supported |= SUPPORTED_40000baseSR4_Full; + if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_LR4) + ecmd->supported |= SUPPORTED_40000baseLR4_Full; /* With no link speed and duplex are unknown */ ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN); @@ -490,6 +472,37 @@ static int i40e_get_settings(struct net_device *netdev, /* Now set the settings that don't rely on link being up/down */ + /* For backplane, supported and advertised are only reliant on the + * phy types the NVM specifies are supported. + */ + if (hw->device_id == I40E_DEV_ID_KX_B || + hw->device_id == I40E_DEV_ID_KX_C || + hw->device_id == I40E_DEV_ID_20G_KR2 || + hw->device_id == I40E_DEV_ID_20G_KR2_A) { + ecmd->supported = SUPPORTED_Autoneg; + ecmd->advertising = ADVERTISED_Autoneg; + if (hw->phy.phy_types & I40E_CAP_PHY_TYPE_40GBASE_KR4) { + ecmd->supported |= SUPPORTED_40000baseKR4_Full; + ecmd->advertising |= ADVERTISED_40000baseKR4_Full; + } + if (hw->phy.phy_types & I40E_CAP_PHY_TYPE_20GBASE_KR2) { + ecmd->supported |= SUPPORTED_20000baseKR2_Full; + ecmd->advertising |= ADVERTISED_20000baseKR2_Full; + } + if (hw->phy.phy_types & I40E_CAP_PHY_TYPE_10GBASE_KR) { + ecmd->supported |= SUPPORTED_10000baseKR_Full; + ecmd->advertising |= ADVERTISED_10000baseKR_Full; + } + if (hw->phy.phy_types & I40E_CAP_PHY_TYPE_10GBASE_KX4) { + ecmd->supported |= SUPPORTED_10000baseKX4_Full; + ecmd->advertising |= ADVERTISED_10000baseKX4_Full; + } + if (hw->phy.phy_types & I40E_CAP_PHY_TYPE_1000BASE_KX) { + ecmd->supported |= SUPPORTED_1000baseKX_Full; + ecmd->advertising |= ADVERTISED_1000baseKX_Full; + } + } + /* Set autoneg settings */ ecmd->autoneg = ((hw_link_info->an_info & I40E_AQ_AN_COMPLETED) ? AUTONEG_ENABLE : AUTONEG_DISABLE); @@ -589,6 +602,14 @@ static int i40e_set_settings(struct net_device *netdev, hw->phy.link_info.link_info & I40E_AQ_LINK_UP) return -EOPNOTSUPP; + if (hw->device_id == I40E_DEV_ID_KX_B || + hw->device_id == I40E_DEV_ID_KX_C || + hw->device_id == I40E_DEV_ID_20G_KR2 || + hw->device_id == I40E_DEV_ID_20G_KR2_A) { + netdev_info(netdev, "Changing settings is not supported on backplane.\n"); + return -EOPNOTSUPP; + } + /* get our own copy of the bits to check against */ memset(&safe_ecmd, 0, sizeof(struct ethtool_cmd)); i40e_get_settings(netdev, &safe_ecmd); diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index dc80a5f..7158ff2 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -10338,6 +10338,14 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); pf->hw.phy.link_info.requested_speeds = abilities.link_speed; + /* get the supported phy types from the fw */ + err = i40e_aq_get_phy_capabilities(hw, false, true, &abilities, NULL); + if (err) + 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); + /* print a string summarizing features */ i40e_print_features(pf); diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index 0a450ac..6f69576 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -187,14 +187,45 @@ 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; - u32 autoneg_advertised; - u32 phy_id; - u32 module_type; 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; }; #define I40E_HW_CAP_MAX_GPIO 30 diff --git a/drivers/net/ethernet/intel/i40evf/i40e_type.h b/drivers/net/ethernet/intel/i40evf/i40e_type.h index f3155e3..85af3b4 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_type.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_type.h @@ -187,14 +187,45 @@ 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; - u32 autoneg_advertised; - u32 phy_id; - u32 module_type; 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; }; #define I40E_HW_CAP_MAX_GPIO 30 -- cgit v0.10.2 From 06a24dd6c29a814a81595861c2cc681329c56bfc Mon Sep 17 00:00:00 2001 From: Catherine Sullivan Date: Mon, 31 Aug 2015 19:54:55 -0400 Subject: i40e/i40evf: Bump i40e version to 1.3.25 and i40evf to 1.3.17 Bump. Change-ID: If3cd42f6c1b9546beed60faf9c79faab35216f58 Signed-off-by: Catherine Sullivan Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 7158ff2..f728140 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -39,7 +39,7 @@ static const char i40e_driver_string[] = #define DRV_VERSION_MAJOR 1 #define DRV_VERSION_MINOR 3 -#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 7ea7d2e..69767c0 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -34,7 +34,7 @@ char i40evf_driver_name[] = "i40evf"; static const char i40evf_driver_string[] = "Intel(R) XL710/X710 Virtual Function Network Driver"; -#define DRV_VERSION "1.3.13" +#define DRV_VERSION "1.3.17" const char i40evf_driver_version[] = DRV_VERSION; static const char i40evf_copyright[] = "Copyright (c) 2013 - 2015 Intel Corporation."; -- cgit v0.10.2 From f079fa005aae08ee0e1bc32699874ff4f02e11c1 Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Thu, 20 Aug 2015 15:31:20 -0700 Subject: ixgbe: add flow control ethertype to the anti-spoofing filter This patch makes sure that flow control packets initiated by the VF are dropped and reported as spoofed. Flow control packets can be used to limit the throughput or as DOS attack when generated from a VF. Flow control is not supported per VF hence any pause frames generated from a VF are considered malicious. Also cleaned up indentation and some redundant comments. Signed-off-by: Emil Tantilov Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 1910039..c4608f8 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -3723,14 +3723,20 @@ static void ixgbe_configure_virtualization(struct ixgbe_adapter *adapter) hw->mac.ops.set_mac_anti_spoofing(hw, (adapter->num_vfs != 0), adapter->num_vfs); - /* Ensure LLDP is set for Ethertype Antispoofing if we will be + /* Ensure LLDP and FC is set for Ethertype Antispoofing if we will be * calling set_ethertype_anti_spoofing for each VF in loop below */ - if (hw->mac.ops.set_ethertype_anti_spoofing) + if (hw->mac.ops.set_ethertype_anti_spoofing) { IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_LLDP), - (IXGBE_ETQF_FILTER_EN | /* enable filter */ - IXGBE_ETQF_TX_ANTISPOOF | /* tx antispoof */ - IXGBE_ETH_P_LLDP)); /* LLDP eth type */ + (IXGBE_ETQF_FILTER_EN | + IXGBE_ETQF_TX_ANTISPOOF | + IXGBE_ETH_P_LLDP)); + + IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_FC), + (IXGBE_ETQF_FILTER_EN | + IXGBE_ETQF_TX_ANTISPOOF | + ETH_P_PAUSE)); + } /* For VFs that have spoof checking turned off */ for (i = 0; i < adapter->num_vfs; i++) { diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 939c90c..995f031 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -1752,6 +1752,9 @@ enum { * FCoE (0x8906): Filter 2 * 1588 (0x88f7): Filter 3 * FIP (0x8914): Filter 4 + * LLDP (0x88CC): Filter 5 + * LACP (0x8809): Filter 6 + * FC (0x8808): Filter 7 */ #define IXGBE_ETQF_FILTER_EAPOL 0 #define IXGBE_ETQF_FILTER_FCOE 2 @@ -1759,6 +1762,7 @@ enum { #define IXGBE_ETQF_FILTER_FIP 4 #define IXGBE_ETQF_FILTER_LLDP 5 #define IXGBE_ETQF_FILTER_LACP 6 +#define IXGBE_ETQF_FILTER_FC 7 /* VLAN Control Bit Masks */ #define IXGBE_VLNCTRL_VET 0x0000FFFF /* bits 0-15 */ -- cgit v0.10.2 From f6f19f8bb9afcd0e9970fe51b5affa3063af4499 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 24 Aug 2015 17:01:58 -0700 Subject: fm10k: use snprintf() instead of sprintf() to avoid buffer overflow Signed-off-by: Jacob Keller Signed-off-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c b/drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c index 08ecf43..5304bc1 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c @@ -176,7 +176,7 @@ void fm10k_dbg_q_vector_init(struct fm10k_q_vector *q_vector) return; /* Generate a folder for each q_vector */ - sprintf(name, "q_vector.%03d", q_vector->v_idx); + snprintf(name, sizeof(name), "q_vector.%03d", q_vector->v_idx); q_vector->dbg_q_vector = debugfs_create_dir(name, interface->dbg_intfc); if (!q_vector->dbg_q_vector) @@ -186,7 +186,7 @@ void fm10k_dbg_q_vector_init(struct fm10k_q_vector *q_vector) for (i = 0; i < q_vector->tx.count; i++) { struct fm10k_ring *ring = &q_vector->tx.ring[i]; - sprintf(name, "tx_ring.%03d", ring->queue_index); + snprintf(name, sizeof(name), "tx_ring.%03d", ring->queue_index); debugfs_create_file(name, 0600, q_vector->dbg_q_vector, ring, @@ -197,7 +197,7 @@ void fm10k_dbg_q_vector_init(struct fm10k_q_vector *q_vector) for (i = 0; i < q_vector->rx.count; i++) { struct fm10k_ring *ring = &q_vector->rx.ring[i]; - sprintf(name, "rx_ring.%03d", ring->queue_index); + snprintf(name, sizeof(name), "rx_ring.%03d", ring->queue_index); debugfs_create_file(name, 0600, q_vector->dbg_q_vector, ring, diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c index 4ef2fbd..447a5f8 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c @@ -206,13 +206,13 @@ static void fm10k_get_stat_strings(struct net_device *dev, u8 *data) } for (i = 0; i < interface->hw.mac.max_queues; i++) { - sprintf(p, "tx_queue_%u_packets", i); + snprintf(p, ETH_GSTRING_LEN, "tx_queue_%u_packets", i); p += ETH_GSTRING_LEN; - sprintf(p, "tx_queue_%u_bytes", i); + snprintf(p, ETH_GSTRING_LEN, "tx_queue_%u_bytes", i); p += ETH_GSTRING_LEN; - sprintf(p, "rx_queue_%u_packets", i); + snprintf(p, ETH_GSTRING_LEN, "rx_queue_%u_packets", i); p += ETH_GSTRING_LEN; - sprintf(p, "rx_queue_%u_bytes", i); + snprintf(p, ETH_GSTRING_LEN, "rx_queue_%u_bytes", i); p += ETH_GSTRING_LEN; } } -- cgit v0.10.2 From b4a5127b03b7f0b06dcaf08a878ffc93ad53fa82 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 24 Aug 2015 17:02:00 -0700 Subject: fm10k: do not use enum as boolean Check for actual value NETREG_UNINITIALIZED in case it ever changes from the current value of zero. Signed-off-by: Jacob Keller Signed-off-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_main.c b/drivers/net/ethernet/intel/fm10k/fm10k_main.c index 2f47bfe..537d81a 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_main.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_main.c @@ -1905,7 +1905,7 @@ static void fm10k_init_reta(struct fm10k_intfc *interface) u32 reta, base; /* If the netdev is initialized we have to maintain table if possible */ - if (interface->netdev->reg_state) { + if (interface->netdev->reg_state != NETREG_UNINITIALIZED) { for (i = FM10K_RETA_SIZE; i--;) { reta = interface->reta[i]; if ((((reta << 24) >> 24) < rss_i) && -- cgit v0.10.2 From 02a6d6136fa2a17f400a030829a6435556b3e65b Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Wed, 14 Oct 2015 14:25:53 +0200 Subject: Revert "ipv4/icmp: redirect messages can use the ingress daddr as source" Revert the commit e2ca690b657f ("ipv4/icmp: redirect messages can use the ingress daddr as source"), which tried to introduce a more suitable behaviour for ICMP redirect messages generated by VRRP routers. However RFC 5798 section 8.1.1 states: The IPv4 source address of an ICMP redirect should be the address that the end-host used when making its next-hop routing decision. while said commit used the generating packet destination address, which do not match the above and in most cases leads to no redirect packets to be generated. Signed-off-by: Paolo Abeni Signed-off-by: David S. Miller diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 9983825..ebe94f2 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -884,8 +884,8 @@ icmp_ignore_bogus_error_responses - BOOLEAN icmp_errors_use_inbound_ifaddr - BOOLEAN - If zero, icmp error messages except redirects are sent with the primary - address of the exiting interface. + If zero, icmp error messages are sent with the primary address of + the exiting interface. If non-zero, the message will be sent with the primary address of the interface that received the packet that caused the icmp error. @@ -897,23 +897,8 @@ icmp_errors_use_inbound_ifaddr - BOOLEAN then the primary address of the first non-loopback interface that has one will be used regardless of this setting. - The source address selection of icmp redirect messages is controlled by - icmp_errors_use_inbound_ifaddr. Default: 0 -icmp_redirects_use_orig_daddr - BOOLEAN - - If zero, icmp redirect messages are sent using the address specified for - other icmp errors by icmp_errors_use_inbound_ifaddr. - - If non-zero, the message will be sent with the destination address of - the packet that caused the icmp redirect. - This behaviour is the preferred one on VRRP routers (see RFC 5798 - section 8.1.1). - - Default: 0 - - igmp_max_memberships - INTEGER Change the maximum number of multicast groups we can subscribe to. Default: 20 diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 46d336a..c68926b 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -74,7 +74,6 @@ struct netns_ipv4 { int sysctl_icmp_ratelimit; int sysctl_icmp_ratemask; int sysctl_icmp_errors_use_inbound_ifaddr; - int sysctl_icmp_redirects_use_orig_daddr; struct local_ports ip_local_ports; diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index f3c356b..36e2697 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -659,9 +659,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) */ saddr = iph->daddr; - if (!((type == ICMP_REDIRECT) && - net->ipv4.sysctl_icmp_redirects_use_orig_daddr) && - !(rt->rt_flags & RTCF_LOCAL)) { + if (!(rt->rt_flags & RTCF_LOCAL)) { struct net_device *dev = NULL; rcu_read_lock(); @@ -1224,11 +1222,6 @@ static int __net_init icmp_sk_init(struct net *net) net->ipv4.sysctl_icmp_ratemask = 0x1818; net->ipv4.sysctl_icmp_errors_use_inbound_ifaddr = 0; - /* Control paramerer - use the daddr of originating packets as saddr - * in redirect messages? - */ - net->ipv4.sysctl_icmp_redirects_use_orig_daddr = 0; - return 0; fail: diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 30a531c..894da3a 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -818,13 +818,6 @@ static struct ctl_table ipv4_net_table[] = { .proc_handler = proc_dointvec }, { - .procname = "icmp_redirects_use_orig_daddr", - .data = &init_net.ipv4.sysctl_icmp_redirects_use_orig_daddr, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, - { .procname = "icmp_ratelimit", .data = &init_net.ipv4.sysctl_icmp_ratelimit, .maxlen = sizeof(int), -- cgit v0.10.2 From 4568637f7a20bbb1dcbf8ada56de08f6c940bcbd Mon Sep 17 00:00:00 2001 From: yankejian Date: Tue, 13 Oct 2015 09:53:45 +0800 Subject: net: hisilicon: supports promisc mode this patch adds support to set promisc mode. it configs the queue on init seq when it is on promisc mode.and being enabled or disabled promisc mode by upper level user. Signed-off-by: yankejian Signed-off-by: Yisen Zhuang Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h index d4a1eb1..cec95ac 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.h +++ b/drivers/net/ethernet/hisilicon/hns/hnae.h @@ -430,6 +430,7 @@ struct hnae_ae_ops { void (*set_coalesce_usecs)(struct hnae_handle *handle, u32 timeout); int (*set_coalesce_frames)(struct hnae_handle *handle, u32 coalesce_frames); + 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 (*set_mc_addr)(struct hnae_handle *handle, void *addr); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c index a2c72f8..1a16c03 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c @@ -392,6 +392,11 @@ static int hns_ae_set_autoneg(struct hnae_handle *handle, u8 enable) return hns_mac_set_autoneg(hns_get_mac_cb(handle), enable); } +static void hns_ae_set_promisc_mode(struct hnae_handle *handle, u32 en) +{ + hns_dsaf_set_promisc_mode(hns_ae_get_dsaf_dev(handle->dev), en); +} + static int hns_ae_get_autoneg(struct hnae_handle *handle) { u32 auto_neg; @@ -748,6 +753,7 @@ static struct hnae_ae_ops hns_dsaf_ops = { .get_rx_max_coalesced_frames = hns_ae_get_rx_max_coalesced_frames, .set_coalesce_usecs = hns_ae_set_coalesce_usecs, .set_coalesce_frames = hns_ae_set_coalesce_frames, + .set_promisc_mode = hns_ae_set_promisc_mode, .set_mac_addr = hns_ae_set_mac_address, .set_mc_addr = hns_ae_set_multicast_one, .set_mtu = hns_ae_set_mtu, diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c index 26ae6c6..ffc2604 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c @@ -217,6 +217,25 @@ hns_dsaf_ppe_qid_cfg(struct dsaf_device *dsaf_dev, u32 qid_cfg) } } +static void hns_dsaf_mix_def_qid_cfg(struct dsaf_device *dsaf_dev) +{ + u16 max_q_per_vf, max_vfn; + u32 q_id, q_num_per_port; + u32 i; + + hns_rcb_get_queue_mode(dsaf_dev->dsaf_mode, + HNS_DSAF_COMM_SERVICE_NW_IDX, + &max_vfn, &max_q_per_vf); + q_num_per_port = max_vfn * max_q_per_vf; + + for (i = 0, q_id = 0; i < DSAF_SERVICE_NW_NUM; i++) { + dsaf_set_dev_field(dsaf_dev, + DSAF_MIX_DEF_QID_0_REG + 0x0004 * i, + 0xff, 0, q_id); + q_id += q_num_per_port; + } +} + /** * hns_dsaf_sw_port_type_cfg - cfg sw type * @dsaf_id: dsa fabric id @@ -592,6 +611,11 @@ static void hns_dsaf_tbl_tcam_data_ucast_pul( dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul); } +void hns_dsaf_set_promisc_mode(struct dsaf_device *dsaf_dev, u32 en) +{ + dsaf_set_dev_bit(dsaf_dev, DSAF_CFG_0_REG, DSAF_CFG_MIX_MODE_S, !!en); +} + /** * hns_dsaf_tbl_stat_en - tbl * @dsaf_id: dsa fabric id @@ -920,6 +944,9 @@ static void hns_dsaf_comm_init(struct dsaf_device *dsaf_dev) /* set 22 queue per tx ppe engine, only used in switch mode */ hns_dsaf_ppe_qid_cfg(dsaf_dev, DSAF_DEFAUTL_QUEUE_NUM_PER_PPE); + /* set promisc def queue id */ + hns_dsaf_mix_def_qid_cfg(dsaf_dev); + /* in non switch mode, set all port to access mode */ hns_dsaf_sw_port_type_cfg(dsaf_dev, DSAF_SW_PORT_TYPE_NON_VLAN); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h index 315b07e..b2b9348 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h @@ -423,5 +423,6 @@ 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); #endif /* __HNS_DSAF_MAIN_H__ */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c index 05ea244..4db32c6 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c @@ -575,8 +575,8 @@ int hns_rcb_set_coalesced_frames(struct dsaf_device *dsaf_dev, *@max_vfn : max vfn number *@max_q_per_vf:max ring number per vm */ -static void hns_rcb_get_queue_mode(enum dsaf_mode dsaf_mode, int comm_index, - u16 *max_vfn, u16 *max_q_per_vf) +void hns_rcb_get_queue_mode(enum dsaf_mode dsaf_mode, int comm_index, + u16 *max_vfn, u16 *max_q_per_vf) { if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) { switch (dsaf_mode) { diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h index c7db613..3a2afe2 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h @@ -107,6 +107,8 @@ int hns_rcb_common_init_hw(struct rcb_common_cb *rcb_common); void hns_rcb_start(struct hnae_queue *q, u32 val); void hns_rcb_get_cfg(struct rcb_common_cb *rcb_common); void hns_rcb_common_init_commit_hw(struct rcb_common_cb *rcb_common); +void hns_rcb_get_queue_mode(enum dsaf_mode dsaf_mode, int comm_index, + u16 *max_vfn, u16 *max_q_per_vf); void hns_rcb_ring_enable_hw(struct hnae_queue *q, u32 val); void hns_rcb_int_clr_hw(struct hnae_queue *q, u32 flag); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index ce7f2e0..6f9091c 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -1161,6 +1161,21 @@ void hns_set_multicast_list(struct net_device *ndev) } } +void hns_nic_set_rx_mode(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + + if (h->dev->ops->set_promisc_mode) { + if (ndev->flags & IFF_PROMISC) + h->dev->ops->set_promisc_mode(h, 1); + else + h->dev->ops->set_promisc_mode(h, 0); + } + + hns_set_multicast_list(ndev); +} + struct rtnl_link_stats64 *hns_nic_get_stats64(struct net_device *ndev, struct rtnl_link_stats64 *stats) { @@ -1220,7 +1235,7 @@ static const struct net_device_ops hns_nic_netdev_ops = { #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = hns_nic_poll_controller, #endif - .ndo_set_rx_mode = hns_set_multicast_list, + .ndo_set_rx_mode = hns_nic_set_rx_mode, }; static void hns_nic_update_link_status(struct net_device *netdev) -- cgit v0.10.2 From d3074cce904a26bb645d3ee7ab5026cf7ef0a3d1 Mon Sep 17 00:00:00 2001 From: Karen Xie Date: Tue, 13 Oct 2015 17:13:59 -0700 Subject: cxgb4i: Increased the value of MAX_IMM_TX_PKT_LEN from 128 to 256 bytes This helps improving the latency of small packets. Signed-off-by: Rakesh Ranjan Signed-off-by: Karen Xie Signed-off-by: David S. Miller diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c index de6feb8..804806e 100644 --- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c +++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c @@ -160,7 +160,7 @@ static struct scsi_transport_template *cxgb4i_stt; #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) #define RCV_BUFSIZ_MASK 0x3FFU -#define MAX_IMM_TX_PKT_LEN 128 +#define MAX_IMM_TX_PKT_LEN 256 static int push_tx_frames(struct cxgbi_sock *, int); -- cgit v0.10.2 From 7f0abb1f0f2b110ec38ade73c241fcba30f9112a Mon Sep 17 00:00:00 2001 From: lipeng Date: Wed, 14 Oct 2015 10:28:57 +0800 Subject: net: hisilicon net: fix a bug about led this patch fixes a bug in hns driver. the link led is on at the beginning, but at this time the ethernet port is on down status. it needs to reset the led status on init sequence. Signed-off-by: lipeng Signed-off-by: yankejian Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c index 95bf42a..f8f7347 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c @@ -744,9 +744,11 @@ int hns_mac_get_cfg(struct dsaf_device *dsaf_dev, int mac_idx) mac_cb->serdes_vaddr = dsaf_dev->sds_base; if (dsaf_dev->cpld_base && - mac_idx < DSAF_SERVICE_PORT_NUM_PER_DSAF) + mac_idx < DSAF_SERVICE_PORT_NUM_PER_DSAF) { mac_cb->cpld_vaddr = dsaf_dev->cpld_base + mac_cb->mac_id * CPLD_ADDR_PORT_OFFSET; + cpld_led_reset(mac_cb); + } mac_cb->sfp_prsnt = 0; mac_cb->txpkt_for_led = 0; mac_cb->rxpkt_for_led = 0; -- cgit v0.10.2 From fb0801dcc17845ef52a19f9f9fbbb88a1a93f9b6 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 14 Oct 2015 18:30:48 +0800 Subject: net: phy: aquantia/teranetics: Convert to use module_phy_driver macro Use module_phy_driver macro to simplify the code a bit. Signed-off-by: Axel Lin Signed-off-by: David S. Miller diff --git a/drivers/net/phy/aquantia.c b/drivers/net/phy/aquantia.c index d6111af..f1936b7 100644 --- a/drivers/net/phy/aquantia.c +++ b/drivers/net/phy/aquantia.c @@ -171,20 +171,7 @@ static struct phy_driver aquantia_driver[] = { }, }; -static int __init aquantia_init(void) -{ - return phy_drivers_register(aquantia_driver, - ARRAY_SIZE(aquantia_driver)); -} - -static void __exit aquantia_exit(void) -{ - return phy_drivers_unregister(aquantia_driver, - ARRAY_SIZE(aquantia_driver)); -} - -module_init(aquantia_init); -module_exit(aquantia_exit); +module_phy_driver(aquantia_driver); static struct mdio_device_id __maybe_unused aquantia_tbl[] = { { PHY_ID_AQ1202, 0xfffffff0 }, diff --git a/drivers/net/phy/teranetics.c b/drivers/net/phy/teranetics.c index 91e1bec..07463fc 100644 --- a/drivers/net/phy/teranetics.c +++ b/drivers/net/phy/teranetics.c @@ -112,20 +112,7 @@ static struct phy_driver teranetics_driver[] = { }, }; -static int __init teranetics_init(void) -{ - return phy_drivers_register(teranetics_driver, - ARRAY_SIZE(teranetics_driver)); -} - -static void __exit teranetics_exit(void) -{ - return phy_drivers_unregister(teranetics_driver, - ARRAY_SIZE(teranetics_driver)); -} - -module_init(teranetics_init); -module_exit(teranetics_exit); +module_phy_driver(teranetics_driver); static struct mdio_device_id __maybe_unused teranetics_tbl[] = { { PHY_ID_TN2020, 0xffffffff }, -- cgit v0.10.2 From c2f34a65a61cd1ace3b53c93e8b38d2f79f4ff0d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 14 Oct 2015 05:58:38 -0700 Subject: tcp/dccp: fix potential NULL deref in __inet_inherit_port() As we no longer hold listener lock in fast path, it is possible that a child is created right after listener freed its bound port, if a close() is done while incoming packets are processed. __inet_inherit_port() must detect this and return an error, so that caller can free the child earlier. Fixes: e994b2f0fb92 ("tcp: do not lock listener to process SYN packets") Fixes: 079096f103fa ("tcp/dccp: install syn_recv requests into ehash table") Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index 08643a3..958728a 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -137,6 +137,10 @@ int __inet_inherit_port(const struct sock *sk, struct sock *child) spin_lock(&head->lock); tb = inet_csk(sk)->icsk_bind_hash; + if (unlikely(!tb)) { + spin_unlock(&head->lock); + return -ENOENT; + } if (tb->port != port) { /* NOTE: using tproxy and redirecting skbs to a proxy * on a different listener port breaks the assumption -- cgit v0.10.2 From f985c65c908f6b26c30019a83dc5ea295f5fcf62 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 14 Oct 2015 06:16:49 -0700 Subject: tcp: avoid spurious SYN flood detection at listen() time At listen() time, there is a small window where listener is visible with a zero backlog, triggering a spurious "Possible SYN flooding on port" message. Nothing prevents us from setting the correct backlog. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index 3208a65..fd645c4 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h @@ -312,7 +312,7 @@ static inline unsigned int inet_csk_listen_poll(const struct sock *sk) (POLLIN | POLLRDNORM) : 0; } -int inet_csk_listen_start(struct sock *sk, const int nr_table_entries); +int inet_csk_listen_start(struct sock *sk, int backlog); void inet_csk_listen_stop(struct sock *sk); void inet_csk_addr2sockaddr(struct sock *sk, struct sockaddr *uaddr); diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 514b9e9..ba9ec9a 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -727,14 +727,14 @@ void inet_csk_prepare_forced_close(struct sock *sk) } EXPORT_SYMBOL(inet_csk_prepare_forced_close); -int inet_csk_listen_start(struct sock *sk, const int nr_table_entries) +int inet_csk_listen_start(struct sock *sk, int backlog) { struct inet_connection_sock *icsk = inet_csk(sk); struct inet_sock *inet = inet_sk(sk); reqsk_queue_alloc(&icsk->icsk_accept_queue); - sk->sk_max_ack_backlog = 0; + sk->sk_max_ack_backlog = backlog; sk->sk_ack_backlog = 0; inet_csk_delack_init(sk); -- cgit v0.10.2 From fd76ee4da55abb21babfc69310d321b9cb9a32e0 Mon Sep 17 00:00:00 2001 From: Eli Cohen Date: Wed, 14 Oct 2015 17:43:45 +0300 Subject: net/mlx5_core: Fix internal error detection conditions The detection of a fatal condition has been updated to take into account the state reported by the device or by detecting an all ones read of the firmware version which indicates that the device is not accessible. Signed-off-by: Eli Cohen Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx5/core/health.c b/drivers/net/ethernet/mellanox/mlx5/core/health.c index 9b81e1c..f1eb686 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/health.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/health.c @@ -57,6 +57,31 @@ enum { MLX5_HEALTH_SYNDR_HIGH_TEMP = 0x10 }; +enum { + MLX5_NIC_IFC_FULL = 0, + MLX5_NIC_IFC_DISABLED = 1, + MLX5_NIC_IFC_NO_DRAM_NIC = 2 +}; + +static u8 get_nic_interface(struct mlx5_core_dev *dev) +{ + return (ioread32be(&dev->iseg->cmdq_addr_l_sz) >> 8) & 3; +} + +static int in_fatal(struct mlx5_core_dev *dev) +{ + struct mlx5_core_health *health = &dev->priv.health; + struct health_buffer __iomem *h = health->health; + + if (get_nic_interface(dev) == MLX5_NIC_IFC_DISABLED) + return 1; + + if (ioread32be(&h->fw_ver) == 0xffffffff) + return 1; + + return 0; +} + static void health_care(struct work_struct *work) { struct mlx5_core_health *health; @@ -136,11 +161,21 @@ static void print_health_info(struct mlx5_core_dev *dev) dev_err(&dev->pdev->dev, "ext_synd 0x%04x\n", ioread16be(&h->ext_synd)); } +static unsigned long get_next_poll_jiffies(void) +{ + unsigned long next; + + get_random_bytes(&next, sizeof(next)); + next %= HZ; + next += jiffies + MLX5_HEALTH_POLL_INTERVAL; + + return next; +} + static void poll_health(unsigned long data) { struct mlx5_core_dev *dev = (struct mlx5_core_dev *)data; struct mlx5_core_health *health = &dev->priv.health; - unsigned long next; u32 count; count = ioread32be(health->health_counter); @@ -151,14 +186,16 @@ static void poll_health(unsigned long data) health->prev = count; if (health->miss_counter == MAX_MISSES) { - mlx5_core_err(dev, "device's health compromised\n"); + dev_err(&dev->pdev->dev, "device's health compromised - reached miss count\n"); print_health_info(dev); - queue_work(health->wq, &health->work); } else { - get_random_bytes(&next, sizeof(next)); - next %= HZ; - next += jiffies + MLX5_HEALTH_POLL_INTERVAL; - mod_timer(&health->timer, next); + mod_timer(&health->timer, get_next_poll_jiffies()); + } + + if (in_fatal(dev) && !health->sick) { + health->sick = true; + print_health_info(dev); + queue_work(health->wq, &health->work); } } diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 41a3287..62b7d43 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -393,6 +393,7 @@ struct mlx5_core_health { struct timer_list timer; u32 prev; int miss_counter; + bool sick; struct workqueue_struct *wq; struct work_struct work; }; -- cgit v0.10.2 From 89d44f0a6c732db23b219be708e2fe1e03ee4842 Mon Sep 17 00:00:00 2001 From: Majd Dibbiny Date: Wed, 14 Oct 2015 17:43:46 +0300 Subject: net/mlx5_core: Add pci error handlers to mlx5_core driver This patch implement the pci_error_handlers for mlx5_core which allow the driver to recover from PCI error. Once an error is detected in the PCI, the mlx5_pci_err_detected is called and it: 1) Marks the device to be in 'Internal Error' state. 2) Dispatches an event to the mlx5_ib to flush all the outstanding cqes with error. 3) Returns all the on going commands with error. 4) Unloads the driver. Afterwards, the FW is reset and mlx5_pci_slot_reset is called and it enables the device and restore it's pci state. If the later succeeds, mlx5_pci_resume is called, and it loads the SW stack. Signed-off-by: Majd Dibbiny Signed-off-by: Eli Cohen Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index c3e54b7..fabfc9e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -256,8 +256,154 @@ static void dump_buf(void *buf, int size, int data_only, int offset) enum { MLX5_DRIVER_STATUS_ABORTED = 0xfe, + MLX5_DRIVER_SYND = 0xbadd00de, }; +static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op, + u32 *synd, u8 *status) +{ + *synd = 0; + *status = 0; + + switch (op) { + case MLX5_CMD_OP_TEARDOWN_HCA: + case MLX5_CMD_OP_DISABLE_HCA: + case MLX5_CMD_OP_MANAGE_PAGES: + case MLX5_CMD_OP_DESTROY_MKEY: + case MLX5_CMD_OP_DESTROY_EQ: + case MLX5_CMD_OP_DESTROY_CQ: + case MLX5_CMD_OP_DESTROY_QP: + case MLX5_CMD_OP_DESTROY_PSV: + case MLX5_CMD_OP_DESTROY_SRQ: + case MLX5_CMD_OP_DESTROY_XRC_SRQ: + case MLX5_CMD_OP_DESTROY_DCT: + case MLX5_CMD_OP_DEALLOC_Q_COUNTER: + case MLX5_CMD_OP_DEALLOC_PD: + case MLX5_CMD_OP_DEALLOC_UAR: + case MLX5_CMD_OP_DETTACH_FROM_MCG: + case MLX5_CMD_OP_DEALLOC_XRCD: + case MLX5_CMD_OP_DEALLOC_TRANSPORT_DOMAIN: + case MLX5_CMD_OP_DELETE_VXLAN_UDP_DPORT: + case MLX5_CMD_OP_DELETE_L2_TABLE_ENTRY: + case MLX5_CMD_OP_DESTROY_TIR: + case MLX5_CMD_OP_DESTROY_SQ: + case MLX5_CMD_OP_DESTROY_RQ: + case MLX5_CMD_OP_DESTROY_RMP: + case MLX5_CMD_OP_DESTROY_TIS: + case MLX5_CMD_OP_DESTROY_RQT: + case MLX5_CMD_OP_DESTROY_FLOW_TABLE: + case MLX5_CMD_OP_DESTROY_FLOW_GROUP: + case MLX5_CMD_OP_DELETE_FLOW_TABLE_ENTRY: + return MLX5_CMD_STAT_OK; + + case MLX5_CMD_OP_QUERY_HCA_CAP: + case MLX5_CMD_OP_QUERY_ADAPTER: + case MLX5_CMD_OP_INIT_HCA: + case MLX5_CMD_OP_ENABLE_HCA: + case MLX5_CMD_OP_QUERY_PAGES: + case MLX5_CMD_OP_SET_HCA_CAP: + case MLX5_CMD_OP_QUERY_ISSI: + case MLX5_CMD_OP_SET_ISSI: + case MLX5_CMD_OP_CREATE_MKEY: + case MLX5_CMD_OP_QUERY_MKEY: + case MLX5_CMD_OP_QUERY_SPECIAL_CONTEXTS: + case MLX5_CMD_OP_PAGE_FAULT_RESUME: + case MLX5_CMD_OP_CREATE_EQ: + case MLX5_CMD_OP_QUERY_EQ: + case MLX5_CMD_OP_GEN_EQE: + case MLX5_CMD_OP_CREATE_CQ: + case MLX5_CMD_OP_QUERY_CQ: + case MLX5_CMD_OP_MODIFY_CQ: + case MLX5_CMD_OP_CREATE_QP: + case MLX5_CMD_OP_RST2INIT_QP: + case MLX5_CMD_OP_INIT2RTR_QP: + case MLX5_CMD_OP_RTR2RTS_QP: + case MLX5_CMD_OP_RTS2RTS_QP: + case MLX5_CMD_OP_SQERR2RTS_QP: + case MLX5_CMD_OP_2ERR_QP: + case MLX5_CMD_OP_2RST_QP: + case MLX5_CMD_OP_QUERY_QP: + case MLX5_CMD_OP_SQD_RTS_QP: + case MLX5_CMD_OP_INIT2INIT_QP: + case MLX5_CMD_OP_CREATE_PSV: + case MLX5_CMD_OP_CREATE_SRQ: + case MLX5_CMD_OP_QUERY_SRQ: + case MLX5_CMD_OP_ARM_RQ: + case MLX5_CMD_OP_CREATE_XRC_SRQ: + case MLX5_CMD_OP_QUERY_XRC_SRQ: + case MLX5_CMD_OP_ARM_XRC_SRQ: + case MLX5_CMD_OP_CREATE_DCT: + case MLX5_CMD_OP_DRAIN_DCT: + case MLX5_CMD_OP_QUERY_DCT: + case MLX5_CMD_OP_ARM_DCT_FOR_KEY_VIOLATION: + case MLX5_CMD_OP_QUERY_VPORT_STATE: + case MLX5_CMD_OP_MODIFY_VPORT_STATE: + case MLX5_CMD_OP_QUERY_ESW_VPORT_CONTEXT: + case MLX5_CMD_OP_MODIFY_ESW_VPORT_CONTEXT: + case MLX5_CMD_OP_QUERY_NIC_VPORT_CONTEXT: + case MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT: + case MLX5_CMD_OP_QUERY_ROCE_ADDRESS: + case MLX5_CMD_OP_SET_ROCE_ADDRESS: + case MLX5_CMD_OP_QUERY_HCA_VPORT_CONTEXT: + case MLX5_CMD_OP_MODIFY_HCA_VPORT_CONTEXT: + case MLX5_CMD_OP_QUERY_HCA_VPORT_GID: + case MLX5_CMD_OP_QUERY_HCA_VPORT_PKEY: + case MLX5_CMD_OP_QUERY_VPORT_COUNTER: + case MLX5_CMD_OP_ALLOC_Q_COUNTER: + case MLX5_CMD_OP_QUERY_Q_COUNTER: + case MLX5_CMD_OP_ALLOC_PD: + case MLX5_CMD_OP_ALLOC_UAR: + case MLX5_CMD_OP_CONFIG_INT_MODERATION: + case MLX5_CMD_OP_ACCESS_REG: + case MLX5_CMD_OP_ATTACH_TO_MCG: + case MLX5_CMD_OP_GET_DROPPED_PACKET_LOG: + case MLX5_CMD_OP_MAD_IFC: + case MLX5_CMD_OP_QUERY_MAD_DEMUX: + case MLX5_CMD_OP_SET_MAD_DEMUX: + case MLX5_CMD_OP_NOP: + case MLX5_CMD_OP_ALLOC_XRCD: + case MLX5_CMD_OP_ALLOC_TRANSPORT_DOMAIN: + case MLX5_CMD_OP_QUERY_CONG_STATUS: + case MLX5_CMD_OP_MODIFY_CONG_STATUS: + case MLX5_CMD_OP_QUERY_CONG_PARAMS: + case MLX5_CMD_OP_MODIFY_CONG_PARAMS: + case MLX5_CMD_OP_QUERY_CONG_STATISTICS: + case MLX5_CMD_OP_ADD_VXLAN_UDP_DPORT: + case MLX5_CMD_OP_SET_L2_TABLE_ENTRY: + case MLX5_CMD_OP_QUERY_L2_TABLE_ENTRY: + case MLX5_CMD_OP_CREATE_TIR: + case MLX5_CMD_OP_MODIFY_TIR: + case MLX5_CMD_OP_QUERY_TIR: + case MLX5_CMD_OP_CREATE_SQ: + case MLX5_CMD_OP_MODIFY_SQ: + case MLX5_CMD_OP_QUERY_SQ: + case MLX5_CMD_OP_CREATE_RQ: + case MLX5_CMD_OP_MODIFY_RQ: + case MLX5_CMD_OP_QUERY_RQ: + case MLX5_CMD_OP_CREATE_RMP: + case MLX5_CMD_OP_MODIFY_RMP: + case MLX5_CMD_OP_QUERY_RMP: + case MLX5_CMD_OP_CREATE_TIS: + case MLX5_CMD_OP_MODIFY_TIS: + case MLX5_CMD_OP_QUERY_TIS: + case MLX5_CMD_OP_CREATE_RQT: + case MLX5_CMD_OP_MODIFY_RQT: + case MLX5_CMD_OP_QUERY_RQT: + case MLX5_CMD_OP_CREATE_FLOW_TABLE: + 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_SET_FLOW_TABLE_ENTRY: + case MLX5_CMD_OP_QUERY_FLOW_TABLE_ENTRY: + *status = MLX5_DRIVER_STATUS_ABORTED; + *synd = MLX5_DRIVER_SYND; + return -EIO; + default: + mlx5_core_err(dev, "Unknown FW command (%d)\n", op); + return -EINVAL; + } +} + const char *mlx5_command_str(int command) { switch (command) { @@ -592,6 +738,16 @@ static int wait_func(struct mlx5_core_dev *dev, struct mlx5_cmd_work_ent *ent) return err; } +static __be32 *get_synd_ptr(struct mlx5_outbox_hdr *out) +{ + return &out->syndrome; +} + +static u8 *get_status_ptr(struct mlx5_outbox_hdr *out) +{ + return &out->status; +} + /* Notes: * 1. Callback functions may not sleep * 2. page queue commands do not support asynchrous completion @@ -1200,6 +1356,11 @@ static struct mlx5_cmd_msg *alloc_msg(struct mlx5_core_dev *dev, int in_size, return msg; } +static u16 opcode_from_in(struct mlx5_inbox_hdr *in) +{ + return be16_to_cpu(in->opcode); +} + static int is_manage_pages(struct mlx5_inbox_hdr *in) { return be16_to_cpu(in->opcode) == MLX5_CMD_OP_MANAGE_PAGES; @@ -1214,6 +1375,15 @@ static int cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out, gfp_t gfp; int err; u8 status = 0; + u32 drv_synd; + + if (pci_channel_offline(dev->pdev) || + dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) { + err = mlx5_internal_err_ret_value(dev, opcode_from_in(in), &drv_synd, &status); + *get_synd_ptr(out) = cpu_to_be32(drv_synd); + *get_status_ptr(out) = status; + return err; + } pages_queue = is_manage_pages(in); gfp = callback ? GFP_ATOMIC : GFP_KERNEL; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/health.c b/drivers/net/ethernet/mellanox/mlx5/core/health.c index f1eb686..f5deb64 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/health.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/health.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include "mlx5_core.h" @@ -68,6 +69,29 @@ static u8 get_nic_interface(struct mlx5_core_dev *dev) return (ioread32be(&dev->iseg->cmdq_addr_l_sz) >> 8) & 3; } +static void trigger_cmd_completions(struct mlx5_core_dev *dev) +{ + unsigned long flags; + u64 vector; + + /* wait for pending handlers to complete */ + synchronize_irq(dev->priv.msix_arr[MLX5_EQ_VEC_CMD].vector); + spin_lock_irqsave(&dev->cmd.alloc_lock, flags); + vector = ~dev->cmd.bitmask & ((1ul << (1 << dev->cmd.log_sz)) - 1); + if (!vector) + goto no_trig; + + vector |= MLX5_TRIGGERED_CMD_COMP; + spin_unlock_irqrestore(&dev->cmd.alloc_lock, flags); + + mlx5_core_dbg(dev, "vector 0x%llx\n", vector); + mlx5_cmd_comp_handler(dev, vector); + return; + +no_trig: + spin_unlock_irqrestore(&dev->cmd.alloc_lock, flags); +} + static int in_fatal(struct mlx5_core_dev *dev) { struct mlx5_core_health *health = &dev->priv.health; @@ -82,6 +106,43 @@ static int in_fatal(struct mlx5_core_dev *dev) return 0; } +void mlx5_enter_error_state(struct mlx5_core_dev *dev) +{ + if (dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) + return; + + mlx5_core_err(dev, "start\n"); + if (pci_channel_offline(dev->pdev) || in_fatal(dev)) + dev->state = MLX5_DEVICE_STATE_INTERNAL_ERROR; + + mlx5_core_event(dev, MLX5_DEV_EVENT_SYS_ERROR, 0); + mlx5_core_err(dev, "end\n"); +} + +static void mlx5_handle_bad_state(struct mlx5_core_dev *dev) +{ + u8 nic_interface = get_nic_interface(dev); + + switch (nic_interface) { + case MLX5_NIC_IFC_FULL: + mlx5_core_warn(dev, "Expected to see disabled NIC but it is full driver\n"); + break; + + case MLX5_NIC_IFC_DISABLED: + mlx5_core_warn(dev, "starting teardown\n"); + break; + + case MLX5_NIC_IFC_NO_DRAM_NIC: + mlx5_core_warn(dev, "Expected to see disabled NIC but it is no dram nic\n"); + break; + default: + mlx5_core_warn(dev, "Expected to see disabled NIC but it is has invalid value %d\n", + nic_interface); + } + + mlx5_disable_device(dev); +} + static void health_care(struct work_struct *work) { struct mlx5_core_health *health; @@ -92,6 +153,7 @@ static void health_care(struct work_struct *work) priv = container_of(health, struct mlx5_priv, health); dev = container_of(priv, struct mlx5_core_dev, priv); mlx5_core_warn(dev, "handling bad device here\n"); + mlx5_handle_bad_state(dev); } static const char *hsynd_str(u8 synd) @@ -147,6 +209,10 @@ static void print_health_info(struct mlx5_core_dev *dev) u32 fw; int i; + /* If the syndrom is 0, the device is OK and no need to print buffer */ + if (!ioread8(&h->synd)) + return; + for (i = 0; i < ARRAY_SIZE(h->assert_var); i++) dev_err(&dev->pdev->dev, "assert_var[%d] 0x%08x\n", i, ioread32be(h->assert_var + i)); @@ -178,6 +244,12 @@ static void poll_health(unsigned long data) struct mlx5_core_health *health = &dev->priv.health; u32 count; + if (dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) { + trigger_cmd_completions(dev); + mod_timer(&health->timer, get_next_poll_jiffies()); + return; + } + count = ioread32be(health->health_counter); if (count == health->prev) ++health->miss_counter; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index b6edc58..a103a54 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include "mlx5_core.h" @@ -181,6 +182,34 @@ static int set_dma_caps(struct pci_dev *pdev) return err; } +static int mlx5_pci_enable_device(struct mlx5_core_dev *dev) +{ + struct pci_dev *pdev = dev->pdev; + int err = 0; + + mutex_lock(&dev->pci_status_mutex); + if (dev->pci_status == MLX5_PCI_STATUS_DISABLED) { + err = pci_enable_device(pdev); + if (!err) + dev->pci_status = MLX5_PCI_STATUS_ENABLED; + } + mutex_unlock(&dev->pci_status_mutex); + + return err; +} + +static void mlx5_pci_disable_device(struct mlx5_core_dev *dev) +{ + struct pci_dev *pdev = dev->pdev; + + mutex_lock(&dev->pci_status_mutex); + if (dev->pci_status == MLX5_PCI_STATUS_ENABLED) { + pci_disable_device(pdev); + dev->pci_status = MLX5_PCI_STATUS_DISABLED; + } + mutex_unlock(&dev->pci_status_mutex); +} + static int request_bar(struct pci_dev *pdev) { int err = 0; @@ -807,7 +836,7 @@ static int mlx5_pci_init(struct mlx5_core_dev *dev, struct mlx5_priv *priv) if (!priv->dbg_root) return -ENOMEM; - err = pci_enable_device(pdev); + err = mlx5_pci_enable_device(dev); if (err) { dev_err(&pdev->dev, "Cannot enable PCI device, aborting\n"); goto err_dbg; @@ -841,7 +870,7 @@ err_clr_master: pci_clear_master(dev->pdev); release_bar(dev->pdev); err_disable: - pci_disable_device(dev->pdev); + mlx5_pci_disable_device(dev); err_dbg: debugfs_remove(priv->dbg_root); @@ -853,7 +882,7 @@ static void mlx5_pci_close(struct mlx5_core_dev *dev, struct mlx5_priv *priv) iounmap(dev->iseg); pci_clear_master(dev->pdev); release_bar(dev->pdev); - pci_disable_device(dev->pdev); + mlx5_pci_disable_device(dev); debugfs_remove(priv->dbg_root); } @@ -863,13 +892,25 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv) struct pci_dev *pdev = dev->pdev; int err; + mutex_lock(&dev->intf_state_mutex); + if (dev->interface_state == MLX5_INTERFACE_STATE_UP) { + dev_warn(&dev->pdev->dev, "%s: interface is up, NOP\n", + __func__); + goto out; + } + dev_info(&pdev->dev, "firmware version: %d.%d.%d\n", fw_rev_maj(dev), fw_rev_min(dev), fw_rev_sub(dev)); + /* on load removing any previous indication of internal error, device is + * up + */ + dev->state = MLX5_DEVICE_STATE_UP; + err = mlx5_cmd_init(dev); if (err) { dev_err(&pdev->dev, "Failed initializing command interface, aborting\n"); - return err; + goto out_err; } mlx5_pagealloc_init(dev); @@ -994,6 +1035,10 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv) if (err) pr_info("failed request module on %s\n", MLX5_IB_MOD); + dev->interface_state = MLX5_INTERFACE_STATE_UP; +out: + mutex_unlock(&dev->intf_state_mutex); + return 0; err_reg_dev: @@ -1024,7 +1069,7 @@ err_stop_poll: mlx5_stop_health_poll(dev); if (mlx5_cmd_teardown_hca(dev)) { dev_err(&dev->pdev->dev, "tear_down_hca failed, skip cleanup\n"); - return err; + goto out_err; } err_pagealloc_stop: @@ -1040,13 +1085,23 @@ err_pagealloc_cleanup: mlx5_pagealloc_cleanup(dev); mlx5_cmd_cleanup(dev); +out_err: + dev->state = MLX5_DEVICE_STATE_INTERNAL_ERROR; + mutex_unlock(&dev->intf_state_mutex); + return err; } static int mlx5_unload_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv) { - int err; + int err = 0; + mutex_lock(&dev->intf_state_mutex); + if (dev->interface_state == MLX5_INTERFACE_STATE_DOWN) { + dev_warn(&dev->pdev->dev, "%s: interface is down, NOP\n", + __func__); + goto out; + } mlx5_unregister_device(dev); mlx5_cleanup_mr_table(dev); mlx5_cleanup_srq_table(dev); @@ -1072,10 +1127,12 @@ static int mlx5_unload_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv) mlx5_cmd_cleanup(dev); out: + dev->interface_state = MLX5_INTERFACE_STATE_DOWN; + mutex_unlock(&dev->intf_state_mutex); return err; } -static void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event, +void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event, unsigned long param) { struct mlx5_priv *priv = &dev->priv; @@ -1125,6 +1182,8 @@ static int init_one(struct pci_dev *pdev, INIT_LIST_HEAD(&priv->ctx_list); spin_lock_init(&priv->ctx_lock); + mutex_init(&dev->pci_status_mutex); + mutex_init(&dev->intf_state_mutex); err = mlx5_pci_init(dev, priv); if (err) { dev_err(&pdev->dev, "mlx5_pci_init failed with error code %d\n", err); @@ -1172,6 +1231,112 @@ static void remove_one(struct pci_dev *pdev) kfree(dev); } +static pci_ers_result_t mlx5_pci_err_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + struct mlx5_core_dev *dev = pci_get_drvdata(pdev); + struct mlx5_priv *priv = &dev->priv; + + dev_info(&pdev->dev, "%s was called\n", __func__); + mlx5_enter_error_state(dev); + mlx5_unload_one(dev, priv); + mlx5_pci_disable_device(dev); + return state == pci_channel_io_perm_failure ? + PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_NEED_RESET; +} + +static pci_ers_result_t mlx5_pci_slot_reset(struct pci_dev *pdev) +{ + struct mlx5_core_dev *dev = pci_get_drvdata(pdev); + int err = 0; + + dev_info(&pdev->dev, "%s was called\n", __func__); + + err = mlx5_pci_enable_device(dev); + if (err) { + dev_err(&pdev->dev, "%s: mlx5_pci_enable_device failed with error code: %d\n" + , __func__, err); + return PCI_ERS_RESULT_DISCONNECT; + } + pci_set_master(pdev); + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + + return err ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED; +} + +void mlx5_disable_device(struct mlx5_core_dev *dev) +{ + mlx5_pci_err_detected(dev->pdev, 0); +} + +/* wait for the device to show vital signs. For now we check + * that we can read the device ID and that the health buffer + * shows a non zero value which is different than 0xffffffff + */ +static void wait_vital(struct pci_dev *pdev) +{ + struct mlx5_core_dev *dev = pci_get_drvdata(pdev); + struct mlx5_core_health *health = &dev->priv.health; + const int niter = 100; + u32 count; + u16 did; + int i; + + /* Wait for firmware to be ready after reset */ + msleep(1000); + for (i = 0; i < niter; i++) { + if (pci_read_config_word(pdev, 2, &did)) { + dev_warn(&pdev->dev, "failed reading config word\n"); + break; + } + if (did == pdev->device) { + dev_info(&pdev->dev, "device ID correctly read after %d iterations\n", i); + break; + } + msleep(50); + } + if (i == niter) + dev_warn(&pdev->dev, "%s-%d: could not read device ID\n", __func__, __LINE__); + + for (i = 0; i < niter; i++) { + count = ioread32be(health->health_counter); + if (count && count != 0xffffffff) { + dev_info(&pdev->dev, "Counter value 0x%x after %d iterations\n", count, i); + break; + } + msleep(50); + } + + if (i == niter) + dev_warn(&pdev->dev, "%s-%d: could not read device ID\n", __func__, __LINE__); +} + +static void mlx5_pci_resume(struct pci_dev *pdev) +{ + struct mlx5_core_dev *dev = pci_get_drvdata(pdev); + struct mlx5_priv *priv = &dev->priv; + int err; + + dev_info(&pdev->dev, "%s was called\n", __func__); + + pci_save_state(pdev); + wait_vital(pdev); + + err = mlx5_load_one(dev, priv); + if (err) + dev_err(&pdev->dev, "%s: mlx5_load_one failed with error code: %d\n" + , __func__, err); + else + dev_info(&pdev->dev, "%s: device recovered\n", __func__); +} + +static const struct pci_error_handlers mlx5_err_handler = { + .error_detected = mlx5_pci_err_detected, + .slot_reset = mlx5_pci_slot_reset, + .resume = mlx5_pci_resume +}; + static const struct pci_device_id mlx5_core_pci_table[] = { { PCI_VDEVICE(MELLANOX, 0x1011) }, /* Connect-IB */ { PCI_VDEVICE(MELLANOX, 0x1012) }, /* Connect-IB VF */ @@ -1188,7 +1353,8 @@ static struct pci_driver mlx5_core_driver = { .name = DRIVER_NAME, .id_table = mlx5_core_pci_table, .probe = init_one, - .remove = remove_one + .remove = remove_one, + .err_handler = &mlx5_err_handler }; static int __init init(void) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index 30c0be7..cee5b7a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -86,6 +86,10 @@ int mlx5_query_hca_caps(struct mlx5_core_dev *dev); int mlx5_query_board_id(struct mlx5_core_dev *dev); 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_enter_error_state(struct mlx5_core_dev *dev); +void mlx5_disable_device(struct mlx5_core_dev *dev); void mlx5e_init(void); void mlx5e_cleanup(void); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c index 76432a5..1cda5d2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c @@ -493,15 +493,20 @@ int mlx5_reclaim_startup_pages(struct mlx5_core_dev *dev) struct fw_page *fwp; struct rb_node *p; int nclaimed = 0; - int err; + int err = 0; do { p = rb_first(&dev->priv.page_root); if (p) { fwp = rb_entry(p, struct fw_page, rb_node); - err = reclaim_pages(dev, fwp->func_id, - optimal_reclaimed_pages(), - &nclaimed); + if (dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) { + free_4k(dev, fwp->addr); + nclaimed = 1; + } else { + err = reclaim_pages(dev, fwp->func_id, + optimal_reclaimed_pages(), + &nclaimed); + } if (err) { mlx5_core_warn(dev, "failed reclaiming pages (%d)\n", err); diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 62b7d43..9aba8d51 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -487,8 +487,26 @@ struct mlx5_priv { spinlock_t ctx_lock; }; +enum mlx5_device_state { + MLX5_DEVICE_STATE_UP, + MLX5_DEVICE_STATE_INTERNAL_ERROR, +}; + +enum mlx5_interface_state { + MLX5_INTERFACE_STATE_DOWN, + MLX5_INTERFACE_STATE_UP, +}; + +enum mlx5_pci_status { + MLX5_PCI_STATUS_DISABLED, + MLX5_PCI_STATUS_ENABLED, +}; + struct mlx5_core_dev { struct pci_dev *pdev; + /* sync pci state */ + struct mutex pci_status_mutex; + enum mlx5_pci_status pci_status; u8 rev_id; char board_id[MLX5_BOARD_ID_LEN]; struct mlx5_cmd cmd; @@ -497,6 +515,10 @@ struct mlx5_core_dev { u32 hca_caps_max[MLX5_CAP_NUM][MLX5_UN_SZ_DW(hca_cap_union)]; phys_addr_t iseg_base; struct mlx5_init_seg __iomem *iseg; + enum mlx5_device_state state; + /* sync interface state */ + struct mutex intf_state_mutex; + enum mlx5_interface_state interface_state; void (*event) (struct mlx5_core_dev *dev, enum mlx5_dev_event event, unsigned long param); -- cgit v0.10.2 From e3297246c2c8cf8548ba722da3e3a8104cdcd035 Mon Sep 17 00:00:00 2001 From: Eli Cohen Date: Wed, 14 Oct 2015 17:43:47 +0300 Subject: net/mlx5_core: Wait for FW readiness on startup On device initialization, wait till firmware indicates that that it is done with initialization before proceeding to initialize the device. Also update initialization segment layout to match driver/firmware interface definitions. Signed-off-by: Eli Cohen Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index a103a54..2388aec 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -152,6 +153,25 @@ static struct mlx5_profile profile[] = { }, }; +#define FW_INIT_TIMEOUT_MILI 2000 +#define FW_INIT_WAIT_MS 2 + +static int wait_fw_init(struct mlx5_core_dev *dev, u32 max_wait_mili) +{ + unsigned long end = jiffies + msecs_to_jiffies(max_wait_mili); + int err = 0; + + while (fw_initializing(dev)) { + if (time_after(jiffies, end)) { + err = -EBUSY; + break; + } + msleep(FW_INIT_WAIT_MS); + } + + return err; +} + static int set_dma_caps(struct pci_dev *pdev) { int err; @@ -913,6 +933,13 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv) goto out_err; } + err = wait_fw_init(dev, FW_INIT_TIMEOUT_MILI); + if (err) { + dev_err(&dev->pdev->dev, "Firmware over %d MS in initializing state, aborting\n", + FW_INIT_TIMEOUT_MILI); + goto out_err; + } + mlx5_pagealloc_init(dev); err = mlx5_core_enable_hca(dev); diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h index 2a0b956..0b473cb 100644 --- a/include/linux/mlx5/device.h +++ b/include/linux/mlx5/device.h @@ -439,7 +439,8 @@ struct mlx5_init_seg { __be32 cmdq_addr_h; __be32 cmdq_addr_l_sz; __be32 cmd_dbell; - __be32 rsvd1[121]; + __be32 rsvd1[120]; + __be32 initializing; struct health_buffer health; __be32 rsvd2[884]; __be32 health_counter; diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 9aba8d51..5c857f2 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -826,6 +826,11 @@ void mlx5_core_put_rsc(struct mlx5_core_rsc_common *common); int mlx5_query_odp_caps(struct mlx5_core_dev *dev, struct mlx5_odp_caps *odp_caps); +static inline int fw_initializing(struct mlx5_core_dev *dev) +{ + return ioread32be(&dev->iseg->initializing) >> 31; +} + static inline u32 mlx5_mkey_to_idx(u32 mkey) { return mkey >> 8; -- cgit v0.10.2 From 2b3ddf27f48c8061f0676c5a8796008099945280 Mon Sep 17 00:00:00 2001 From: Jack Morgenstein Date: Wed, 14 Oct 2015 17:43:48 +0300 Subject: net/mlx4_core: Replace VF zero mac with random mac in mlx4_core By design, when no default MAC addresses are set in the Hypervisor for VFs, the VFs are passed zero-macs. When such a MAC is received by the VF, it generates a random MAC address and registers that MAC address with the Hypervisor. This random mac generation is currently done in the mlx4_en module. There is a problem, though, if the mlx4_ib module is loaded by a VF before the mlx4_en module. In this case, for RoCE, mlx4_ib will see the un-replaced zero-mac and register that zero-mac as part of QP1 initialization. Having a zero-mac in the port's MAC table creates problems for a Baseboard Management Console. The BMC occasionally sends packets with a zero-mac destination MAC. If there is a zero-mac present in the port's MAC table, the FW will send such BMC packets to the host driver rather than to the wire, and BMC will stop working. To address this problem, we move the replacement of zero-mac addresses with random-mac addresses to procedure mlx4_slave_cap(), which is part of the driver startup for VFs, and is before activation of mlx4_ib and mlx4_en. As a result, zero-mac addresses will never be registered in the port MAC table by the driver. In addition, when mlx4_en does initialize the net device, it needs to set the NET_ADDR_RANDOM flag in the netdev structure if the address was randomly generated. This is done so that udev on the VM does not create a new device name after each VF probe (VM boot and such). To accomplish this, we add a per-port flag in mlx4_dev which gets set whenever mlx4_core replaces a zero-mac with a randomly-generated mac. This flag is examined when mlx4_en initializes the net-device. Fix was suggested by Matan Barak Signed-off-by: Jack Morgenstein Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 597d892..886e1bc 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -2816,7 +2816,6 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, struct mlx4_en_priv *priv; int i; int err; - u64 mac_u64; dev = alloc_etherdev_mqs(sizeof(struct mlx4_en_priv), MAX_TX_RINGS, MAX_RX_RINGS); @@ -2908,17 +2907,17 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, dev->addr_len = ETH_ALEN; mlx4_en_u64_to_mac(dev->dev_addr, mdev->dev->caps.def_mac[priv->port]); if (!is_valid_ether_addr(dev->dev_addr)) { - if (mlx4_is_slave(priv->mdev->dev)) { - eth_hw_addr_random(dev); - en_warn(priv, "Assigned random MAC address %pM\n", dev->dev_addr); - mac_u64 = mlx4_mac_to_u64(dev->dev_addr); - mdev->dev->caps.def_mac[priv->port] = mac_u64; - } else { - en_err(priv, "Port: %d, invalid mac burned: %pM, quiting\n", - priv->port, dev->dev_addr); - err = -EINVAL; - goto out; - } + en_err(priv, "Port: %d, invalid mac burned: %pM, quiting\n", + priv->port, dev->dev_addr); + err = -EINVAL; + goto out; + } else if (mlx4_is_slave(priv->mdev->dev) && + (priv->mdev->dev->port_random_macs & 1 << priv->port)) { + /* Random MAC was assigned in mlx4_slave_cap + * in mlx4_core module + */ + dev->addr_assign_type |= NET_ADDR_RANDOM; + en_warn(priv, "Assigned random MAC address %pM\n", dev->dev_addr); } memcpy(priv->current_mac, dev->dev_addr, sizeof(priv->current_mac)); diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index e8ec1de..f13a4d7 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -2840,3 +2840,19 @@ int set_phv_bit(struct mlx4_dev *dev, u8 port, int new_val) return -EOPNOTSUPP; } EXPORT_SYMBOL(set_phv_bit); + +void mlx4_replace_zero_macs(struct mlx4_dev *dev) +{ + int i; + u8 mac_addr[ETH_ALEN]; + + dev->port_random_macs = 0; + for (i = 1; i <= dev->caps.num_ports; ++i) + if (!dev->caps.def_mac[i] && + dev->caps.port_type[i] == MLX4_PORT_TYPE_ETH) { + eth_random_addr(mac_addr); + dev->port_random_macs |= 1 << i; + dev->caps.def_mac[i] = mlx4_mac_to_u64(mac_addr); + } +} +EXPORT_SYMBOL_GPL(mlx4_replace_zero_macs); diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 006757f..bcbdfab 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -863,6 +863,8 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) return -ENODEV; } + mlx4_replace_zero_macs(dev); + dev->caps.qp0_qkey = kcalloc(dev->caps.num_ports, sizeof(u32), GFP_KERNEL); dev->caps.qp0_tunnel = kcalloc(dev->caps.num_ports, sizeof (u32), GFP_KERNEL); dev->caps.qp0_proxy = kcalloc(dev->caps.num_ports, sizeof (u32), GFP_KERNEL); diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 232b2b5..e1cf903 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -1378,6 +1378,8 @@ void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work); void mlx4_init_quotas(struct mlx4_dev *dev); +/* for VFs, replace zero MACs with randomly-generated MACs at driver start */ +void mlx4_replace_zero_macs(struct mlx4_dev *dev); int mlx4_get_slave_num_gids(struct mlx4_dev *dev, int slave, int port); /* Returns the VF index of slave */ int mlx4_get_vf_indx(struct mlx4_dev *dev, int slave); diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index baad4cb..5a8677b 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -833,6 +833,7 @@ struct mlx4_dev { struct mlx4_quotas quotas; struct radix_tree_root qp_table_tree; u8 rev_id; + u8 port_random_macs; char board_id[MLX4_BOARD_ID_LEN]; int numa_node; int oper_log_mgm_entry_size; -- cgit v0.10.2 From 72bfd32d2f84d26aa132dd74a8eef14d039d326f Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Mon, 24 Aug 2015 18:08:31 -0700 Subject: ixgbe: disable LRO by default This patch disables LRO by default in favor of GRO. LRO is incompatible with forwarding and is disabled when forwarding is turned on which makes the default offloads of the driver inconsistent. LRO can still be enabled via ethtool. Signed-off-by: Emil Tantilov Tested-by: Darin Miller Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index c4608f8..1b2ad39 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -5307,7 +5307,6 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter) rss = min_t(int, ixgbe_max_rss_indices(adapter), num_online_cpus()); adapter->ring_feature[RING_F_RSS].limit = rss; adapter->flags2 |= IXGBE_FLAG2_RSC_CAPABLE; - adapter->flags2 |= IXGBE_FLAG2_RSC_ENABLED; adapter->max_q_vectors = MAX_Q_VECTORS_82599; adapter->atr_sample_rate = 20; fdir = min_t(int, IXGBE_MAX_FDIR_INDICES, num_online_cpus()); @@ -5333,7 +5332,6 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter) switch (hw->mac.type) { case ixgbe_mac_82598EB: adapter->flags2 &= ~IXGBE_FLAG2_RSC_CAPABLE; - adapter->flags2 &= ~IXGBE_FLAG2_RSC_ENABLED; if (hw->device_id == IXGBE_DEV_ID_82598AT) adapter->flags |= IXGBE_FLAG_FAN_FAIL_CAPABLE; -- cgit v0.10.2 From 8bf7a7b879985321c63e3ae46fee4e7f0d654ab1 Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Wed, 26 Aug 2015 14:10:22 -0700 Subject: ixgbe: Fix CS4227-related semaphore error on reset failure If the reset never completes, it is necessary to retake the semaphore before returning, because the caller will release the semaphore. Signed-off-by: Mark Rustad Tested-by: Darin Miller Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index ed7b289..06b15edd 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -198,6 +198,7 @@ static s32 ixgbe_write_pe(struct ixgbe_hw *hw, u8 reg, u8 value) * ixgbe_reset_cs4227 - Reset CS4227 using port expander * @hw: pointer to hardware structure * + * This function assumes that the caller has acquired the proper semaphore. * Returns error code */ static s32 ixgbe_reset_cs4227(struct ixgbe_hw *hw) @@ -296,6 +297,14 @@ static void ixgbe_check_cs4227(struct ixgbe_hw *hw) hw->mac.ops.release_swfw_sync(hw, swfw_mask); msleep(IXGBE_CS4227_CHECK_DELAY); } + /* If still pending, assume other instance failed. */ + if (retry == IXGBE_CS4227_RETRIES) { + status = hw->mac.ops.acquire_swfw_sync(hw, swfw_mask); + if (status) { + hw_err(hw, "semaphore failed with %d\n", status); + return; + } + } /* Reset the CS4227. */ status = ixgbe_reset_cs4227(hw); -- cgit v0.10.2 From d4b2f9fe608320604748c3cb90c99b2255782935 Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Thu, 3 Sep 2015 17:18:48 -0400 Subject: i40e: Add missing parameter comment to ndo_bridge_setlink Add nlflags to the function comment for ndo_bridge_setlink. Change-ID: I34c704f307f2a3f7bac3ca4b44e2a094d3d082d6 Signed-off-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index f728140..10662f6 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -8330,6 +8330,7 @@ static int i40e_ndo_bridge_setlink(struct net_device *dev, * @seq: RTNL message seq # * @dev: the netdev being configured * @filter_mask: unused + * @nlflags: netlink flags passed in * * Return the mode in which the hardware bridge is operating in * i.e VEB or VEPA. -- cgit v0.10.2 From ef17178cf5abe304b2098a50f580e8c19e5084d2 Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Thu, 3 Sep 2015 17:18:49 -0400 Subject: i40e: use priv flags to control flow director Some customers wish to be able to control our hardware specific feature called flow director, at runtime. This patch enables ethtool priv flags to control this driver/hardware specific feature. ethtool --set-priv-flags ethX flow-director-atr off NOTE: the ethtool ntuple interface controls the flow-director sideband rules. Change-ID: Iba156350b07fa2ce66f53ded51739f9a3781fe0e Signed-off-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index cfe8f83..c8591b9 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -102,6 +102,7 @@ /* Ethtool Private Flags */ #define I40E_PRIV_FLAGS_NPAR_FLAG BIT(0) #define I40E_PRIV_FLAGS_LINKPOLL_FLAG BIT(1) +#define I40E_PRIV_FLAGS_FD_ATR BIT(2) #define I40E_NVM_VERSION_LO_SHIFT 0 #define I40E_NVM_VERSION_LO_MASK (0xff << I40E_NVM_VERSION_LO_SHIFT) diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 831f971..185ac91 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -231,6 +231,7 @@ static const char i40e_gstrings_test[][ETH_GSTRING_LEN] = { static const char i40e_priv_flags_strings[][ETH_GSTRING_LEN] = { "NPAR", "LinkPolling", + "flow-director-atr", }; #define I40E_PRIV_FLAGS_STR_LEN ARRAY_SIZE(i40e_priv_flags_strings) @@ -2666,6 +2667,8 @@ static u32 i40e_get_priv_flags(struct net_device *dev) I40E_PRIV_FLAGS_NPAR_FLAG : 0; ret_flags |= pf->flags & I40E_FLAG_LINK_POLLING_ENABLED ? I40E_PRIV_FLAGS_LINKPOLL_FLAG : 0; + ret_flags |= pf->flags & I40E_FLAG_FD_ATR_ENABLED ? + I40E_PRIV_FLAGS_FD_ATR : 0; return ret_flags; } @@ -2686,6 +2689,17 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags) else pf->flags &= ~I40E_FLAG_LINK_POLLING_ENABLED; + /* allow the user to control the state of the Flow + * Director ATR (Application Targeted Routing) feature + * of the driver + */ + if (flags & I40E_PRIV_FLAGS_FD_ATR) { + pf->flags |= I40E_FLAG_FD_ATR_ENABLED; + } else { + pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED; + pf->auto_disable_flags |= I40E_FLAG_FD_ATR_ENABLED; + } + return 0; } -- cgit v0.10.2 From 8279e49531f427ae7e8db894072d91bc42d3eeb5 Mon Sep 17 00:00:00 2001 From: Neerav Parikh Date: Thu, 3 Sep 2015 17:18:50 -0400 Subject: i40e: Change some messages from info to debug only There are several error messages that have been printing when there is no functional issue. These messages should be available at debug message level only. Change-ID: Id91e47bf942c483563995f30d8705fa53acd5aa3 Signed-off-by: Neerav Parikh Signed-off-by: Catherine Sullivan Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c b/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c index 7c42d13..886e667 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c @@ -240,10 +240,9 @@ static void i40e_dcbnl_del_app(struct i40e_pf *pf, for (v = 0; v < pf->num_alloc_vsi; v++) { if (pf->vsi[v] && pf->vsi[v]->netdev) { err = i40e_dcbnl_vsi_del_app(pf->vsi[v], app); - if (err) - dev_info(&pf->pdev->dev, "Failed deleting app for VSI seid=%d err=%d sel=%d proto=0x%x prio=%d\n", - pf->vsi[v]->seid, err, app->selector, - app->protocolid, app->priority); + dev_dbg(&pf->pdev->dev, "Deleting app for VSI seid=%d err=%d sel=%d proto=0x%x prio=%d\n", + pf->vsi[v]->seid, err, app->selector, + app->protocolid, app->priority); } } } diff --git a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c b/drivers/net/ethernet/intel/i40e/i40e_fcoe.c index eaedc1f..2ec2411 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c +++ b/drivers/net/ethernet/intel/i40e/i40e_fcoe.c @@ -284,7 +284,7 @@ void i40e_init_pf_fcoe(struct i40e_pf *pf) pf->fcoe_hmc_filt_num = 0; if (!pf->hw.func_caps.fcoe) { - dev_info(&pf->pdev->dev, "FCoE capability is disabled\n"); + dev_dbg(&pf->pdev->dev, "FCoE capability is disabled\n"); return; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 10662f6..7c4add8 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -6593,9 +6593,9 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit) /* make sure our flow control settings are restored */ ret = i40e_set_fc(&pf->hw, &set_fc_aq_fail, true); if (ret) - dev_info(&pf->pdev->dev, "set fc fail, err %s aq_err %s\n", - i40e_stat_str(&pf->hw, ret), - i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); + dev_dbg(&pf->pdev->dev, "setting flow control: ret = %s last_status = %s\n", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); /* Rebuild the VSIs and VEBs that existed before reset. * They are still in our local switch element arrays, so only @@ -9847,8 +9847,14 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf) } pf->queues_left = queues_left; + dev_dbg(&pf->pdev->dev, + "qs_avail=%d FD SB=%d lan_qs=%d lan_tc0=%d vf=%d*%d vmdq=%d*%d, remaining=%d\n", + pf->hw.func_caps.num_tx_qp, + !!(pf->flags & I40E_FLAG_FD_SB_ENABLED), + pf->num_lan_qps, pf->rss_size, pf->num_req_vfs, pf->num_vf_qps, + pf->num_vmdq_vsis, pf->num_vmdq_qps, queues_left); #ifdef I40E_FCOE - dev_info(&pf->pdev->dev, "fcoe queues = %d\n", pf->num_fcoe_qps); + dev_dbg(&pf->pdev->dev, "fcoe queues = %d\n", pf->num_fcoe_qps); #endif } @@ -10333,10 +10339,9 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* get the requested speeds from the fw */ err = i40e_aq_get_phy_capabilities(hw, false, false, &abilities, NULL); if (err) - dev_info(&pf->pdev->dev, - "get phy capabilities failed, err %s aq_err %s, advertised speed settings may not be correct\n", - i40e_stat_str(&pf->hw, err), - i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); + dev_dbg(&pf->pdev->dev, "get requested speeds 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.link_info.requested_speeds = abilities.link_speed; /* get the supported phy types from the fw */ -- cgit v0.10.2 From 3b6c2179eebf4536d08cf966c9f585dd95c72367 Mon Sep 17 00:00:00 2001 From: Catherine Sullivan Date: Thu, 3 Sep 2015 17:18:52 -0400 Subject: i40e: Remove 100M SGMII unless hw is X722 Only the X722 device now supports 100M SGMII, and nothing supports 100M on 1000Base_T. Change-ID: I6f44dcd818944edd40041410e6de380f4a359a0c Signed-off-by: Catherine Sullivan Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 185ac91..b311391 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -255,7 +255,8 @@ static void i40e_partition_setting_complaint(struct i40e_pf *pf) **/ static void i40e_get_settings_link_up(struct i40e_hw *hw, struct ethtool_cmd *ecmd, - struct net_device *netdev) + struct net_device *netdev, + struct i40e_pf *pf) { struct i40e_link_status *hw_link_info = &hw->phy.link_info; u32 link_speed = hw_link_info->link_speed; @@ -299,16 +300,18 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw, break; case I40E_PHY_TYPE_10GBASE_T: case I40E_PHY_TYPE_1000BASE_T: - case I40E_PHY_TYPE_100BASE_TX: ecmd->supported = SUPPORTED_Autoneg | SUPPORTED_10000baseT_Full | - SUPPORTED_1000baseT_Full | - SUPPORTED_100baseT_Full; + SUPPORTED_1000baseT_Full; ecmd->advertising = ADVERTISED_Autoneg; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) ecmd->advertising |= ADVERTISED_10000baseT_Full; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) ecmd->advertising |= ADVERTISED_1000baseT_Full; + break; + case I40E_PHY_TYPE_100BASE_TX: + ecmd->supported = SUPPORTED_Autoneg | + SUPPORTED_100baseT_Full; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_100MB) ecmd->advertising |= ADVERTISED_100baseT_Full; break; @@ -328,12 +331,15 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw, break; case I40E_PHY_TYPE_SGMII: ecmd->supported = SUPPORTED_Autoneg | - SUPPORTED_1000baseT_Full | - SUPPORTED_100baseT_Full; + SUPPORTED_1000baseT_Full; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) ecmd->advertising |= ADVERTISED_1000baseT_Full; - if (hw_link_info->requested_speeds & I40E_LINK_SPEED_100MB) - ecmd->advertising |= ADVERTISED_100baseT_Full; + if (pf->hw.mac.type == I40E_MAC_X722) { + ecmd->supported |= SUPPORTED_100baseT_Full; + if (hw_link_info->requested_speeds & + I40E_LINK_SPEED_100MB) + ecmd->advertising |= ADVERTISED_100baseT_Full; + } break; /* Backplane is set based on supported phy types in get_settings * so don't set anything here but don't warn either @@ -381,7 +387,8 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw, * Reports link settings that can be determined when link is down **/ static void i40e_get_settings_link_down(struct i40e_hw *hw, - struct ethtool_cmd *ecmd) + struct ethtool_cmd *ecmd, + struct i40e_pf *pf) { enum i40e_aq_capabilities_phy_type phy_types = hw->phy.phy_types; @@ -392,11 +399,13 @@ static void i40e_get_settings_link_down(struct i40e_hw *hw, ecmd->advertising = 0x0; if (phy_types & I40E_CAP_PHY_TYPE_SGMII) { ecmd->supported |= SUPPORTED_Autoneg | - SUPPORTED_1000baseT_Full | - SUPPORTED_100baseT_Full; + SUPPORTED_1000baseT_Full; ecmd->advertising |= ADVERTISED_Autoneg | - ADVERTISED_1000baseT_Full | - ADVERTISED_100baseT_Full; + ADVERTISED_1000baseT_Full; + if (pf->hw.mac.type == I40E_MAC_X722) { + ecmd->supported |= SUPPORTED_100baseT_Full; + ecmd->advertising |= ADVERTISED_100baseT_Full; + } } if (phy_types & I40E_CAP_PHY_TYPE_XAUI || phy_types & I40E_CAP_PHY_TYPE_XFI || @@ -425,7 +434,8 @@ static void i40e_get_settings_link_down(struct i40e_hw *hw, ecmd->advertising |= ADVERTISED_Autoneg | ADVERTISED_40000baseCR4_Full; } - if (phy_types & I40E_CAP_PHY_TYPE_100BASE_TX) { + if ((phy_types & I40E_CAP_PHY_TYPE_100BASE_TX) && + !(phy_types & I40E_CAP_PHY_TYPE_1000BASE_T)) { ecmd->supported |= SUPPORTED_Autoneg | SUPPORTED_100baseT_Full; ecmd->advertising |= ADVERTISED_Autoneg | @@ -467,9 +477,9 @@ static int i40e_get_settings(struct net_device *netdev, bool link_up = hw_link_info->link_info & I40E_AQ_LINK_UP; if (link_up) - i40e_get_settings_link_up(hw, ecmd, netdev); + i40e_get_settings_link_up(hw, ecmd, netdev, pf); else - i40e_get_settings_link_down(hw, ecmd); + i40e_get_settings_link_down(hw, ecmd, pf); /* Now set the settings that don't rely on link being up/down */ -- cgit v0.10.2 From 1f012279163af2f1bec30de42960f0312be3a52e Mon Sep 17 00:00:00 2001 From: Anjali Singhai Jain Date: Thu, 3 Sep 2015 17:18:53 -0400 Subject: i40e/i40evf: Add WB_ON_ITR offload support X722 has a way to work around the descriptor WB issue, this offload helps turn that feature on. Change-ID: I7ffa67622426bfca5a651417b63e3afcfeb60412 Signed-off-by: Anjali Singhai Jain Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h index 95d0f8c..ae87982 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h @@ -150,6 +150,7 @@ struct i40e_virtchnl_vsi_resource { #define I40E_VIRTCHNL_VF_OFFLOAD_FCOE 0x00000004 #define I40E_VIRTCHNL_VF_OFFLOAD_RSS_AQ 0x00000008 #define I40E_VIRTCHNL_VF_OFFLOAD_RSS_REG 0x00000010 +#define I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR 0x00000020 #define I40E_VIRTCHNL_VF_OFFLOAD_VLAN 0x00010000 #define I40E_VIRTCHNL_VF_OFFLOAD_RX_POLLING 0x00020000 diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index ae7548a..fd99cd2 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -1203,6 +1203,10 @@ static int i40e_vc_get_vf_resources_msg(struct i40e_vf *vf, u8 *msg) } else { vfres->vf_offload_flags |= I40E_VIRTCHNL_VF_OFFLOAD_RSS_REG; } + + if (vf->driver_caps & I40E_VIRTCHNL_VF_OFFLOAD_RX_POLLING) + vfres->vf_offload_flags |= I40E_VIRTCHNL_VF_OFFLOAD_RX_POLLING; + vfres->num_vsis = num_vsis; vfres->num_queue_pairs = vf->num_queue_pairs; vfres->max_vectors = pf->hw.func_caps.num_msix_vectors_vf; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h index cadda64..9f7b279 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h @@ -150,6 +150,7 @@ struct i40e_virtchnl_vsi_resource { #define I40E_VIRTCHNL_VF_OFFLOAD_FCOE 0x00000004 #define I40E_VIRTCHNL_VF_OFFLOAD_RSS_AQ 0x00000008 #define I40E_VIRTCHNL_VF_OFFLOAD_RSS_REG 0x00000010 +#define I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR 0x00000020 #define I40E_VIRTCHNL_VF_OFFLOAD_VLAN 0x00010000 #define I40E_VIRTCHNL_VF_OFFLOAD_RX_POLLING 0x00020000 diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 69767c0..feb9ad5 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -1123,6 +1123,8 @@ static int i40evf_alloc_queues(struct i40evf_adapter *adapter) tx_ring->netdev = adapter->netdev; tx_ring->dev = &adapter->pdev->dev; tx_ring->count = adapter->tx_desc_count; + if (adapter->flags & I40E_FLAG_WB_ON_ITR_CAPABLE) + tx_ring->flags |= I40E_TXR_FLAGS_WB_ON_ITR; adapter->tx_rings[i] = tx_ring; rx_ring = &tx_ring[1]; @@ -2275,6 +2277,9 @@ static void i40evf_init_task(struct work_struct *work) if (err) goto err_sw_init; i40evf_map_rings_to_vectors(adapter); + if (adapter->vf_res->vf_offload_flags & + I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR) + adapter->flags |= I40EVF_FLAG_WB_ON_ITR_CAPABLE; if (!RSS_AQ(adapter)) i40evf_configure_rss(adapter); err = i40evf_request_misc_irq(adapter); diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index 4f056ef..32e620e 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -156,7 +156,8 @@ int i40evf_send_vf_config_msg(struct i40evf_adapter *adapter) caps = I40E_VIRTCHNL_VF_OFFLOAD_L2 | I40E_VIRTCHNL_VF_OFFLOAD_RSS_AQ | I40E_VIRTCHNL_VF_OFFLOAD_RSS_REG | - I40E_VIRTCHNL_VF_OFFLOAD_VLAN; + I40E_VIRTCHNL_VF_OFFLOAD_VLAN | + I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR; adapter->current_op = I40E_VIRTCHNL_OP_GET_VF_RESOURCES; adapter->aq_required &= ~I40EVF_FLAG_AQ_GET_CONFIG; if (PF_IS_V11(adapter)) -- cgit v0.10.2 From 9b28ef01003f815d9bbdb50c4208586c9246ad08 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Thu, 3 Sep 2015 17:18:54 -0400 Subject: i40e: remove obsolete version check This version check only applies to very, very old firmware, that only ran on A0 hardware, which we never shipped and don't support in this driver anyway. Remove it, before somebody gets hurt. Change-ID: I3752d090ff488acf98ee76b075af961e9c968ee4 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 7c4add8..c4f26eb 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -6353,12 +6353,6 @@ static int i40e_get_capabilities(struct i40e_pf *pf) } } while (err); - if (((pf->hw.aq.fw_maj_ver == 2) && (pf->hw.aq.fw_min_ver < 22)) || - (pf->hw.aq.fw_maj_ver < 2)) { - pf->hw.func_caps.num_msix_vectors++; - pf->hw.func_caps.num_msix_vectors_vf++; - } - if (pf->hw.debug_mask & I40E_DEBUG_USER) dev_info(&pf->pdev->dev, "pf=%d, num_vfs=%d, msix_pf=%d, msix_vf=%d, fd_g=%d, fd_b=%d, pf_max_q=%d num_vsi=%d\n", -- cgit v0.10.2 From 6eae9c6a3536f3a676b54ebf82244c23d3b1019f Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Thu, 3 Sep 2015 17:18:55 -0400 Subject: i40e: allow FD SB if MFP mode only has 1 partition Even though the device might be in MFP mode, if there's only one partition enabled, then we still have plenty of interrupts for managing the Flow Directory Sideband activity. This patch enables FD SB in this case. This patch also reverses the sense of the conditional in order to remove the negative logic. Change-ID: I9edf211a6219fc8d159b4be9964f9fd7f4e00bc0 Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index c4f26eb..0f8ab65 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -7949,12 +7949,12 @@ static int i40e_sw_init(struct i40e_pf *pf) (pf->hw.func_caps.fd_filters_best_effort > 0)) { pf->flags |= I40E_FLAG_FD_ATR_ENABLED; pf->atr_sample_rate = I40E_DEFAULT_ATR_SAMPLE_RATE; - if (!(pf->flags & I40E_FLAG_MFP_ENABLED)) { - pf->flags |= I40E_FLAG_FD_SB_ENABLED; - } else { + if (pf->flags & I40E_FLAG_MFP_ENABLED && + pf->hw.num_partitions > 1) dev_info(&pf->pdev->dev, "Flow Director Sideband mode Disabled in MFP mode\n"); - } + else + pf->flags |= I40E_FLAG_FD_SB_ENABLED; pf->fdir_pf_filter_count = pf->hw.func_caps.fd_filters_guaranteed; pf->hw.fdir_shared_filter_count = -- cgit v0.10.2 From 106b1941ff304ad6b6b9828b24c8545814a7a4e4 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Thu, 3 Sep 2015 17:18:56 -0400 Subject: i40e: remove FD atr control from debugfs Since the flow-director-atr priv flag was added to our ethtool interface, we don't need the on/off control in debugfs. Change-ID: Ib3b599916434ab30ccd40074e71d7a81609b5bb5 Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c index c1dd248..d15bd62 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c +++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c @@ -953,24 +953,6 @@ static void i40e_dbg_dump_veb_all(struct i40e_pf *pf) } } -/** - * i40e_dbg_cmd_fd_ctrl - Enable/disable FD sideband/ATR - * @pf: the PF that would be altered - * @flag: flag that needs enabling or disabling - * @enable: Enable/disable FD SD/ATR - **/ -static void i40e_dbg_cmd_fd_ctrl(struct i40e_pf *pf, u64 flag, bool enable) -{ - if (enable) { - pf->flags |= flag; - } else { - pf->flags &= ~flag; - pf->auto_disable_flags |= flag; - } - dev_info(&pf->pdev->dev, "requesting a PF reset\n"); - i40e_do_reset_safe(pf, BIT(__I40E_PF_RESET_REQUESTED)); -} - #define I40E_MAX_DEBUG_OUT_BUFFER (4096*4) /** * i40e_dbg_command_write - write into command datum @@ -1759,10 +1741,6 @@ static ssize_t i40e_dbg_command_write(struct file *filp, raw_packet = NULL; kfree(asc_packet); asc_packet = NULL; - } else if (strncmp(cmd_buf, "fd-atr off", 10) == 0) { - i40e_dbg_cmd_fd_ctrl(pf, I40E_FLAG_FD_ATR_ENABLED, false); - } else if (strncmp(cmd_buf, "fd-atr on", 9) == 0) { - i40e_dbg_cmd_fd_ctrl(pf, I40E_FLAG_FD_ATR_ENABLED, true); } else if (strncmp(cmd_buf, "fd current cnt", 14) == 0) { dev_info(&pf->pdev->dev, "FD current total filter count for this interface: %d\n", i40e_get_current_fd_count(pf)); @@ -1989,8 +1967,6 @@ static ssize_t i40e_dbg_command_write(struct file *filp, dev_info(&pf->pdev->dev, " send indirect aq_cmd \n"); dev_info(&pf->pdev->dev, " add fd_filter \n"); dev_info(&pf->pdev->dev, " rem fd_filter \n"); - dev_info(&pf->pdev->dev, " fd-atr off\n"); - dev_info(&pf->pdev->dev, " fd-atr on\n"); dev_info(&pf->pdev->dev, " fd current cnt"); dev_info(&pf->pdev->dev, " lldp start\n"); dev_info(&pf->pdev->dev, " lldp stop\n"); -- cgit v0.10.2 From 874d1a10eb1ab466f48627cdd268335a635dde52 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Thu, 3 Sep 2015 17:18:57 -0400 Subject: i40e/i40evf: remove redundant declarations of a variable and a function Remove a variable declaration inside an if block hiding an existing declaration at the start of the function. Also remove a forward function declaration that is no longer needed due to code re-organization. Change-ID: I12954668b722718074949c93d74cd20eaacd93e4 Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index b311391..ed70a3f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -90,9 +90,6 @@ static const struct i40e_stats i40e_gstrings_misc_stats[] = { I40E_VSI_STAT("tx_linearize", tx_linearize), }; -static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, - struct ethtool_rxnfc *cmd); - /* These PF_STATs might look like duplicates of some NETDEV_STATs, * but they are separate. This device supports Virtualization, and * as such might have several netdevs supporting VMDq and FCoE going diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index feb9ad5..f156226 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -1628,7 +1628,7 @@ static void i40evf_reset_task(struct work_struct *work) /* extra wait to make sure minimum wait is met */ msleep(I40EVF_RESET_WAIT_MS); if (i == I40EVF_RESET_WAIT_COUNT) { - struct i40evf_mac_filter *f, *ftmp; + struct i40evf_mac_filter *ftmp; struct i40evf_vlan_filter *fv, *fvtmp; /* reset never finished */ -- cgit v0.10.2 From 3ac67d7bfa2f35cc8a52bdff39e3bbadb6e639a9 Mon Sep 17 00:00:00 2001 From: Kevin Scott Date: Thu, 3 Sep 2015 17:18:58 -0400 Subject: i40e: Store off PHY capabilities Store off reported PHY capabilities in link_info structure. Change-ID: Ife0f037c26983ca985dbf79abf33f8f8791369e8 Signed-off-by: Kevin Scott Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index ffd1cd0..d1ee174 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -1617,6 +1617,9 @@ 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) + hw->phy.phy_types = le32_to_cpu(abilities->phy_type); + return status; } -- cgit v0.10.2 From 3fced535079a6aaff2d85bd5c52ad50df0558e3d Mon Sep 17 00:00:00 2001 From: Anjali Singhai Jain Date: Thu, 3 Sep 2015 17:18:59 -0400 Subject: i40e: X722 is on the IOSF bus and does not report the PCI bus info X722 will report Gen 1x1 in the PCI config space as it is on IOSF bus, so skip the PCI bus link/speed check. Change-ID: Icd5f5751dc7fb00dccf0d5dc5a0a644948e7062e Signed-off-by: Anjali Singhai Jain Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index c8591b9..d587a05 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -333,6 +333,7 @@ struct i40e_pf { #define I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE BIT_ULL(38) #define I40E_FLAG_LINK_POLLING_ENABLED BIT_ULL(39) #define I40E_FLAG_VEB_MODE_ENABLED BIT_ULL(40) +#define I40E_FLAG_NO_PCI_LINK_CHECK BIT_ULL(42) /* tracks features that get auto disabled by errors */ u64 auto_disable_flags; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 0f8ab65..2217078 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -10308,26 +10308,55 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) i40e_fcoe_vsi_setup(pf); #endif - /* Get the negotiated link width and speed from PCI config space */ - pcie_capability_read_word(pf->pdev, PCI_EXP_LNKSTA, &link_status); - - i40e_set_pci_config_data(hw, link_status); - - dev_info(&pdev->dev, "PCI-Express: %s %s\n", - (hw->bus.speed == i40e_bus_speed_8000 ? "Speed 8.0GT/s" : - hw->bus.speed == i40e_bus_speed_5000 ? "Speed 5.0GT/s" : - hw->bus.speed == i40e_bus_speed_2500 ? "Speed 2.5GT/s" : - "Unknown"), - (hw->bus.width == i40e_bus_width_pcie_x8 ? "Width x8" : - hw->bus.width == i40e_bus_width_pcie_x4 ? "Width x4" : - hw->bus.width == i40e_bus_width_pcie_x2 ? "Width x2" : - hw->bus.width == i40e_bus_width_pcie_x1 ? "Width x1" : - "Unknown")); - - if (hw->bus.width < i40e_bus_width_pcie_x8 || - hw->bus.speed < i40e_bus_speed_8000) { - dev_warn(&pdev->dev, "PCI-Express bandwidth available for this device may be insufficient for optimal performance.\n"); - dev_warn(&pdev->dev, "Please move the device to a different PCI-e link with more lanes and/or higher transfer rate.\n"); +#define PCI_SPEED_SIZE 8 +#define PCI_WIDTH_SIZE 8 + /* Devices on the IOSF bus do not have this information + * and will report PCI Gen 1 x 1 by default so don't bother + * checking them. + */ + if (!(pf->flags & I40E_FLAG_NO_PCI_LINK_CHECK)) { + char speed[PCI_SPEED_SIZE] = "Unknown"; + char width[PCI_WIDTH_SIZE] = "Unknown"; + + /* Get the negotiated link width and speed from PCI config + * space + */ + pcie_capability_read_word(pf->pdev, PCI_EXP_LNKSTA, + &link_status); + + i40e_set_pci_config_data(hw, link_status); + + switch (hw->bus.speed) { + case i40e_bus_speed_8000: + strncpy(speed, "8.0", PCI_SPEED_SIZE); break; + case i40e_bus_speed_5000: + strncpy(speed, "5.0", PCI_SPEED_SIZE); break; + case i40e_bus_speed_2500: + strncpy(speed, "2.5", PCI_SPEED_SIZE); break; + default: + break; + } + switch (hw->bus.width) { + case i40e_bus_width_pcie_x8: + strncpy(width, "8", PCI_WIDTH_SIZE); break; + case i40e_bus_width_pcie_x4: + strncpy(width, "4", PCI_WIDTH_SIZE); break; + case i40e_bus_width_pcie_x2: + strncpy(width, "2", PCI_WIDTH_SIZE); break; + case i40e_bus_width_pcie_x1: + strncpy(width, "1", PCI_WIDTH_SIZE); break; + default: + break; + } + + dev_info(&pdev->dev, "PCI-Express: Speed %sGT/s Width x%s\n", + speed, width); + + if (hw->bus.width < i40e_bus_width_pcie_x8 || + hw->bus.speed < i40e_bus_speed_8000) { + dev_warn(&pdev->dev, "PCI-Express bandwidth available for this device may be insufficient for optimal performance.\n"); + dev_warn(&pdev->dev, "Please move the device to a different PCI-e link with more lanes and/or higher transfer rate.\n"); + } } /* get the requested speeds from the fw */ -- cgit v0.10.2 From a916549029848f1356bf2d797e83218de53f17b2 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Thu, 3 Sep 2015 17:19:00 -0400 Subject: i40e: remove unnecessary string copy operations Save a little stack space and remove unnecessary strncpy() with a little string pointer. Change-ID: Id2719d34710bfc273d3bb445fec085cd04276e88 Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 2217078..e090c9c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -4844,8 +4844,8 @@ out: */ void i40e_print_link_message(struct i40e_vsi *vsi, bool isup) { - char speed[SPEED_SIZE] = "Unknown"; - char fc[FC_SIZE] = "RX/TX"; + char *speed = "Unknown"; + char *fc = "Unknown"; if (vsi->current_isup == isup) return; @@ -4866,19 +4866,19 @@ void i40e_print_link_message(struct i40e_vsi *vsi, bool isup) switch (vsi->back->hw.phy.link_info.link_speed) { case I40E_LINK_SPEED_40GB: - strlcpy(speed, "40 Gbps", SPEED_SIZE); + speed = "40 G"; break; case I40E_LINK_SPEED_20GB: - strncpy(speed, "20 Gbps", SPEED_SIZE); + speed = "20 G"; break; case I40E_LINK_SPEED_10GB: - strlcpy(speed, "10 Gbps", SPEED_SIZE); + speed = "10 G"; break; case I40E_LINK_SPEED_1GB: - strlcpy(speed, "1000 Mbps", SPEED_SIZE); + speed = "1000 M"; break; case I40E_LINK_SPEED_100MB: - strncpy(speed, "100 Mbps", SPEED_SIZE); + speed = "100 M"; break; default: break; @@ -4886,20 +4886,20 @@ void i40e_print_link_message(struct i40e_vsi *vsi, bool isup) switch (vsi->back->hw.fc.current_mode) { case I40E_FC_FULL: - strlcpy(fc, "RX/TX", FC_SIZE); + fc = "RX/TX"; break; case I40E_FC_TX_PAUSE: - strlcpy(fc, "TX", FC_SIZE); + fc = "TX"; break; case I40E_FC_RX_PAUSE: - strlcpy(fc, "RX", FC_SIZE); + fc = "RX"; break; default: - strlcpy(fc, "None", FC_SIZE); + fc = "None"; break; } - netdev_info(vsi->netdev, "NIC Link is Up %s Full Duplex, Flow Control: %s\n", + netdev_info(vsi->netdev, "NIC Link is Up %sbps Full Duplex, Flow Control: %s\n", speed, fc); } -- cgit v0.10.2 From 5be8308b1614947f056802cb5775307f0ad54ad2 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Thu, 3 Sep 2015 17:19:01 -0400 Subject: i40evf: speed up init Shorten up the delays in the init task, allowing the VF driver to initialize faster. This aids performance in load/unload tests and mitigates DMAR errors in VF enable/disable tests with absurdly short delays. In the real world, the VF driver will come up more quickly. The original values were set conservatively based on what we expected from the firmware in terms of performance. Now that the driver is in use and we know how well firmware responds to our requests, we can shorten these delays. Change-ID: Ibead77d34b19e8170e667c3f58bc14748bbc5bc9 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index f156226..bccca50 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -2315,7 +2315,7 @@ static void i40evf_init_task(struct work_struct *work) } return; restart: - schedule_delayed_work(&adapter->init_task, msecs_to_jiffies(30)); + schedule_delayed_work(&adapter->init_task, msecs_to_jiffies(20)); return; err_register: @@ -2332,7 +2332,7 @@ err: adapter->flags |= I40EVF_FLAG_PF_COMMS_FAILED; return; /* do not reschedule */ } - schedule_delayed_work(&adapter->init_task, HZ * 3); + schedule_delayed_work(&adapter->init_task, HZ / 2); } /** -- cgit v0.10.2 From 164f739361c962ac63aa6f7cbd48309d1bff15fb Mon Sep 17 00:00:00 2001 From: Catherine Sullivan Date: Thu, 3 Sep 2015 17:19:02 -0400 Subject: i40e/i40evf: Bump i40e version to 1.3.28 and i40evf to 1.3.19 Bump. Change-ID: I8d9a99f320af43960deba8718eee2d6de50eaf46 Signed-off-by: Catherine Sullivan Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index e090c9c..5ed844b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -39,7 +39,7 @@ static const char i40e_driver_string[] = #define DRV_VERSION_MAJOR 1 #define DRV_VERSION_MINOR 3 -#define DRV_VERSION_BUILD 25 +#define DRV_VERSION_BUILD 28 #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 bccca50..429a46c 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -34,7 +34,7 @@ char i40evf_driver_name[] = "i40evf"; static const char i40evf_driver_string[] = "Intel(R) XL710/X710 Virtual Function Network Driver"; -#define DRV_VERSION "1.3.17" +#define DRV_VERSION "1.3.19" const char i40evf_driver_version[] = DRV_VERSION; static const char i40evf_copyright[] = "Copyright (c) 2013 - 2015 Intel Corporation."; -- cgit v0.10.2 From a85ce532f28efabda030d9065a0c2023a2003f36 Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Wed, 9 Sep 2015 13:37:33 -0700 Subject: ixgbe: Check for setup_internal_link method Only call the internal_setup_link method when it is provided. This check is required for newer version parts. Signed-off-by: Mark Rustad Tested-by: Darin Miller Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index 06b15edd..ebe0ac9 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -1617,7 +1617,7 @@ static s32 ixgbe_handle_lasi_ext_t_x550em(struct ixgbe_hw *hw) if (status) return status; - if (lsc) + if (lsc && phy->ops.setup_internal_link) return phy->ops.setup_internal_link(hw); return 0; -- cgit v0.10.2 From adc9048c607a08320e87befc940955d6ffe51fac Mon Sep 17 00:00:00 2001 From: lipeng Date: Thu, 15 Oct 2015 12:40:34 +0800 Subject: net: hisilicon: fixes a bug when using ethtool -S this patch fixes a bug in hns driver. when we want to get statistic info by using ethtool -S, it shows us there are 3 wrong counters info. because the strings related to the registers are wrong. it needs to modify the strings which give us wrong info. Signed-off-by: lipeng Signed-off-by: yankejian Signed-off-by: Yisen Zhuang Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c index dab5ecf..802d554 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c @@ -51,9 +51,9 @@ static const struct mac_stats_string g_xgmac_stats_string[] = { {"xgmac_rx_bad_pkt_from_dsaf", MAC_STATS_FIELD_OFF(rx_bad_from_sw)}, {"xgmac_tx_bad_pkt_64tomax", MAC_STATS_FIELD_OFF(tx_bad_pkts)}, - {"xgmac_rx_not_well_pkt", MAC_STATS_FIELD_OFF(rx_fragment_err)}, - {"xgmac_rx_good_well_pkt", MAC_STATS_FIELD_OFF(rx_undersize)}, - {"xgmac_rx_total_pkt", MAC_STATS_FIELD_OFF(rx_under_min)}, + {"xgmac_rx_bad_pkts_minto64", MAC_STATS_FIELD_OFF(rx_fragment_err)}, + {"xgmac_rx_good_pkts_minto64", MAC_STATS_FIELD_OFF(rx_undersize)}, + {"xgmac_rx_total_pkts_minto64", MAC_STATS_FIELD_OFF(rx_under_min)}, {"xgmac_rx_pkt_64", MAC_STATS_FIELD_OFF(rx_64bytes)}, {"xgmac_rx_pkt_65to127", MAC_STATS_FIELD_OFF(rx_65to127)}, {"xgmac_rx_pkt_128to255", MAC_STATS_FIELD_OFF(rx_128to255)}, -- cgit v0.10.2 From 793f40147e82cdedc80971fa7f5596d6ed1e555e Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 14 Oct 2015 19:40:48 +0200 Subject: switchdev: introduce switchdev deferred ops infrastructure Introduce infrastructure which will be used internally to defer ops. Note that the deferred ops are queued up and either are processed by scheduled work or explicitly by user calling deferred_process function. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/include/net/switchdev.h b/include/net/switchdev.h index 1ce7083..31b9038 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -167,6 +167,7 @@ switchdev_notifier_info_to_dev(const struct switchdev_notifier_info *info) #ifdef CONFIG_NET_SWITCHDEV +void switchdev_deferred_process(void); int switchdev_port_attr_get(struct net_device *dev, struct switchdev_attr *attr); int switchdev_port_attr_set(struct net_device *dev, @@ -208,6 +209,10 @@ void switchdev_port_fwd_mark_set(struct net_device *dev, #else +static inline void switchdev_deferred_process(void) +{ +} + static inline int switchdev_port_attr_get(struct net_device *dev, struct switchdev_attr *attr) { diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index b8aaf820..5e64b59 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -92,6 +93,85 @@ static void switchdev_trans_items_warn_destroy(struct net_device *dev, switchdev_trans_items_destroy(trans); } +static LIST_HEAD(deferred); +static DEFINE_SPINLOCK(deferred_lock); + +typedef void switchdev_deferred_func_t(struct net_device *dev, + const void *data); + +struct switchdev_deferred_item { + struct list_head list; + struct net_device *dev; + switchdev_deferred_func_t *func; + unsigned long data[0]; +}; + +static struct switchdev_deferred_item *switchdev_deferred_dequeue(void) +{ + struct switchdev_deferred_item *dfitem; + + spin_lock_bh(&deferred_lock); + if (list_empty(&deferred)) { + dfitem = NULL; + goto unlock; + } + dfitem = list_first_entry(&deferred, + struct switchdev_deferred_item, list); + list_del(&dfitem->list); +unlock: + spin_unlock_bh(&deferred_lock); + return dfitem; +} + +/** + * switchdev_deferred_process - Process ops in deferred queue + * + * Called to flush the ops currently queued in deferred ops queue. + * rtnl_lock must be held. + */ +void switchdev_deferred_process(void) +{ + struct switchdev_deferred_item *dfitem; + + ASSERT_RTNL(); + + while ((dfitem = switchdev_deferred_dequeue())) { + dfitem->func(dfitem->dev, dfitem->data); + dev_put(dfitem->dev); + kfree(dfitem); + } +} +EXPORT_SYMBOL_GPL(switchdev_deferred_process); + +static void switchdev_deferred_process_work(struct work_struct *work) +{ + rtnl_lock(); + switchdev_deferred_process(); + rtnl_unlock(); +} + +static DECLARE_WORK(deferred_process_work, switchdev_deferred_process_work); + +static int switchdev_deferred_enqueue(struct net_device *dev, + const void *data, size_t data_len, + switchdev_deferred_func_t *func) +{ + struct switchdev_deferred_item *dfitem; + + dfitem = kmalloc(sizeof(*dfitem) + data_len, GFP_ATOMIC); + if (!dfitem) + return -ENOMEM; + dfitem->dev = dev; + dfitem->func = func; + memcpy(dfitem->data, data, data_len); + dev_hold(dev); + spin_lock_bh(&deferred_lock); + list_add_tail(&dfitem->list, &deferred); + spin_unlock_bh(&deferred_lock); + schedule_work(&deferred_process_work); + return 0; +} + /** * switchdev_port_attr_get - Get port attribute * -- cgit v0.10.2 From f7fadf3047d005d17376da65aa9e5734f45a77d4 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 14 Oct 2015 19:40:49 +0200 Subject: switchdev: make struct switchdev_attr parameter const for attr_set calls Signed-off-by: Jiri Pirko Reviewed-by: Vivien Didelot Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c index eafa907..f0e820d 100644 --- a/drivers/net/ethernet/rocker/rocker.c +++ b/drivers/net/ethernet/rocker/rocker.c @@ -4374,7 +4374,7 @@ static int rocker_port_bridge_ageing_time(struct rocker_port *rocker_port, } static int rocker_port_attr_set(struct net_device *dev, - struct switchdev_attr *attr, + const struct switchdev_attr *attr, struct switchdev_trans *trans) { struct rocker_port *rocker_port = netdev_priv(dev); diff --git a/include/net/switchdev.h b/include/net/switchdev.h index 31b9038..d1c7f90 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -132,7 +132,7 @@ struct switchdev_ops { int (*switchdev_port_attr_get)(struct net_device *dev, struct switchdev_attr *attr); int (*switchdev_port_attr_set)(struct net_device *dev, - struct switchdev_attr *attr, + const struct switchdev_attr *attr, struct switchdev_trans *trans); int (*switchdev_port_obj_add)(struct net_device *dev, const struct switchdev_obj *obj, @@ -171,7 +171,7 @@ void switchdev_deferred_process(void); int switchdev_port_attr_get(struct net_device *dev, struct switchdev_attr *attr); int switchdev_port_attr_set(struct net_device *dev, - struct switchdev_attr *attr); + const struct switchdev_attr *attr); int switchdev_port_obj_add(struct net_device *dev, const struct switchdev_obj *obj); int switchdev_port_obj_del(struct net_device *dev, @@ -220,7 +220,7 @@ static inline int switchdev_port_attr_get(struct net_device *dev, } static inline int switchdev_port_attr_set(struct net_device *dev, - struct switchdev_attr *attr) + const struct switchdev_attr *attr) { return -EOPNOTSUPP; } diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 43d7342..84cd863 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -453,7 +453,7 @@ static int dsa_slave_stp_update(struct net_device *dev, u8 state) } static int dsa_slave_port_attr_set(struct net_device *dev, - struct switchdev_attr *attr, + const struct switchdev_attr *attr, struct switchdev_trans *trans) { struct dsa_slave_priv *p = netdev_priv(dev); diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index 5e64b59..23b4e5b 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -215,7 +215,7 @@ int switchdev_port_attr_get(struct net_device *dev, struct switchdev_attr *attr) EXPORT_SYMBOL_GPL(switchdev_port_attr_get); static int __switchdev_port_attr_set(struct net_device *dev, - struct switchdev_attr *attr, + const struct switchdev_attr *attr, struct switchdev_trans *trans) { const struct switchdev_ops *ops = dev->switchdev_ops; @@ -274,7 +274,7 @@ static void switchdev_port_attr_set_work(struct work_struct *work) } static int switchdev_port_attr_set_defer(struct net_device *dev, - struct switchdev_attr *attr) + const struct switchdev_attr *attr) { struct switchdev_attr_set_work *asw; @@ -303,7 +303,8 @@ static int switchdev_port_attr_set_defer(struct net_device *dev, * system is not left in a partially updated state due to * failure from driver/device. */ -int switchdev_port_attr_set(struct net_device *dev, struct switchdev_attr *attr) +int switchdev_port_attr_set(struct net_device *dev, + const struct switchdev_attr *attr) { struct switchdev_trans trans; int err; -- cgit v0.10.2 From 0bc05d585d381c30de3fdf955730df31593d2101 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 14 Oct 2015 19:40:50 +0200 Subject: switchdev: allow caller to explicitly request attr_set as deferred Caller should know if he can call attr_set directly (when holding RTNL) or if he has to defer the att_set processing for later. This also allows drivers to sleep inside attr_set and report operation status back to switchdev core. Switchdev core then warns if status is not ok, instead of silent errors happening in drivers. Benefit from newly introduced switchdev deferred ops infrastructure. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/include/net/switchdev.h b/include/net/switchdev.h index d1c7f90..f7de6f8 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -17,6 +17,7 @@ #define SWITCHDEV_F_NO_RECURSE BIT(0) #define SWITCHDEV_F_SKIP_EOPNOTSUPP BIT(1) +#define SWITCHDEV_F_DEFER BIT(2) struct switchdev_trans_item { struct list_head list; diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index db6d243de..80c34d7 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -41,13 +41,14 @@ void br_set_state(struct net_bridge_port *p, unsigned int state) { struct switchdev_attr attr = { .id = SWITCHDEV_ATTR_ID_PORT_STP_STATE, + .flags = SWITCHDEV_F_DEFER, .u.stp_state = state, }; int err; p->state = state; err = switchdev_port_attr_set(p->dev, &attr); - if (err && err != -EOPNOTSUPP) + if (err) br_warn(p->br, "error setting offload STP state on port %u(%s)\n", (unsigned int) p->port_no, p->dev->name); } diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index 23b4e5b..007b8f4 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -250,75 +250,12 @@ done: return err; } -struct switchdev_attr_set_work { - struct work_struct work; - struct net_device *dev; - struct switchdev_attr attr; -}; - -static void switchdev_port_attr_set_work(struct work_struct *work) -{ - struct switchdev_attr_set_work *asw = - container_of(work, struct switchdev_attr_set_work, work); - int err; - - rtnl_lock(); - err = switchdev_port_attr_set(asw->dev, &asw->attr); - if (err && err != -EOPNOTSUPP) - netdev_err(asw->dev, "failed (err=%d) to set attribute (id=%d)\n", - err, asw->attr.id); - rtnl_unlock(); - - dev_put(asw->dev); - kfree(work); -} - -static int switchdev_port_attr_set_defer(struct net_device *dev, - const struct switchdev_attr *attr) -{ - struct switchdev_attr_set_work *asw; - - asw = kmalloc(sizeof(*asw), GFP_ATOMIC); - if (!asw) - return -ENOMEM; - - INIT_WORK(&asw->work, switchdev_port_attr_set_work); - - dev_hold(dev); - asw->dev = dev; - memcpy(&asw->attr, attr, sizeof(asw->attr)); - - schedule_work(&asw->work); - - return 0; -} - -/** - * switchdev_port_attr_set - Set port attribute - * - * @dev: port device - * @attr: attribute to set - * - * Use a 2-phase prepare-commit transaction model to ensure - * system is not left in a partially updated state due to - * failure from driver/device. - */ -int switchdev_port_attr_set(struct net_device *dev, - const struct switchdev_attr *attr) +static int switchdev_port_attr_set_now(struct net_device *dev, + const struct switchdev_attr *attr) { struct switchdev_trans trans; int err; - if (!rtnl_is_locked()) { - /* Running prepare-commit transaction across stacked - * devices requires nothing moves, so if rtnl_lock is - * not held, schedule a worker thread to hold rtnl_lock - * while setting attr. - */ - - return switchdev_port_attr_set_defer(dev, attr); - } - switchdev_trans_init(&trans); /* Phase I: prepare for attr set. Driver/device should fail @@ -355,6 +292,47 @@ int switchdev_port_attr_set(struct net_device *dev, return err; } + +static void switchdev_port_attr_set_deferred(struct net_device *dev, + const void *data) +{ + const struct switchdev_attr *attr = data; + int err; + + err = switchdev_port_attr_set_now(dev, attr); + if (err && err != -EOPNOTSUPP) + netdev_err(dev, "failed (err=%d) to set attribute (id=%d)\n", + err, attr->id); +} + +static int switchdev_port_attr_set_defer(struct net_device *dev, + const struct switchdev_attr *attr) +{ + return switchdev_deferred_enqueue(dev, attr, sizeof(*attr), + switchdev_port_attr_set_deferred); +} + +/** + * switchdev_port_attr_set - Set port attribute + * + * @dev: port device + * @attr: attribute to set + * + * Use a 2-phase prepare-commit transaction model to ensure + * system is not left in a partially updated state due to + * failure from driver/device. + * + * rtnl_lock must be held and must not be in atomic section, + * in case SWITCHDEV_F_DEFER flag is not set. + */ +int switchdev_port_attr_set(struct net_device *dev, + const struct switchdev_attr *attr) +{ + if (attr->flags & SWITCHDEV_F_DEFER) + return switchdev_port_attr_set_defer(dev, attr); + ASSERT_RTNL(); + return switchdev_port_attr_set_now(dev, attr); +} EXPORT_SYMBOL_GPL(switchdev_port_attr_set); static int __switchdev_port_obj_add(struct net_device *dev, -- cgit v0.10.2 From 850d0cbc9171f63f0418afffb0d89a84db927851 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 14 Oct 2015 19:40:51 +0200 Subject: switchdev: remove pointers from switchdev objects When object is used in deferred work, we cannot use pointers in switchdev object structures because the memory they point at may be already used by someone else. So rather do local copy of the value. Signed-off-by: Jiri Pirko Acked-by: Scott Feldman Reviewed-by: John Fastabend Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c index f0e820d..2cd7435 100644 --- a/drivers/net/ethernet/rocker/rocker.c +++ b/drivers/net/ethernet/rocker/rocker.c @@ -4469,7 +4469,7 @@ static int rocker_port_obj_add(struct net_device *dev, fib4 = SWITCHDEV_OBJ_IPV4_FIB(obj); err = rocker_port_fib_ipv4(rocker_port, trans, htonl(fib4->dst), fib4->dst_len, - fib4->fi, fib4->tb_id, 0); + &fib4->fi, fib4->tb_id, 0); break; case SWITCHDEV_OBJ_ID_PORT_FDB: err = rocker_port_fdb_add(rocker_port, trans, @@ -4541,7 +4541,7 @@ static int rocker_port_obj_del(struct net_device *dev, fib4 = SWITCHDEV_OBJ_IPV4_FIB(obj); err = rocker_port_fib_ipv4(rocker_port, NULL, htonl(fib4->dst), fib4->dst_len, - fib4->fi, fib4->tb_id, + &fib4->fi, fib4->tb_id, ROCKER_OP_FLAG_REMOVE); break; case SWITCHDEV_OBJ_ID_PORT_FDB: @@ -4571,7 +4571,7 @@ static int rocker_port_fdb_dump(const struct rocker_port *rocker_port, hash_for_each_safe(rocker->fdb_tbl, bkt, tmp, found, entry) { if (found->key.rocker_port != rocker_port) continue; - fdb->addr = found->key.addr; + ether_addr_copy(fdb->addr, found->key.addr); fdb->ndm_state = NUD_REACHABLE; fdb->vid = rocker_port_vlan_to_vid(rocker_port, found->key.vlan_id); diff --git a/include/net/switchdev.h b/include/net/switchdev.h index f7de6f8..f8672d7 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -14,6 +14,7 @@ #include #include #include +#include #define SWITCHDEV_F_NO_RECURSE BIT(0) #define SWITCHDEV_F_SKIP_EOPNOTSUPP BIT(1) @@ -59,8 +60,6 @@ struct switchdev_attr { } u; }; -struct fib_info; - enum switchdev_obj_id { SWITCHDEV_OBJ_ID_UNDEFINED, SWITCHDEV_OBJ_ID_PORT_VLAN, @@ -88,7 +87,7 @@ struct switchdev_obj_ipv4_fib { struct switchdev_obj obj; u32 dst; int dst_len; - struct fib_info *fi; + struct fib_info fi; u8 tos; u8 type; u32 nlflags; @@ -101,7 +100,7 @@ struct switchdev_obj_ipv4_fib { /* SWITCHDEV_OBJ_ID_PORT_FDB */ struct switchdev_obj_port_fdb { struct switchdev_obj obj; - const unsigned char *addr; + unsigned char addr[ETH_ALEN]; u16 vid; u16 ndm_state; }; diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index f43ce05..f5e7da0 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -135,10 +135,10 @@ static void fdb_del_external_learn(struct net_bridge_fdb_entry *f) { struct switchdev_obj_port_fdb fdb = { .obj.id = SWITCHDEV_OBJ_ID_PORT_FDB, - .addr = f->addr.addr, .vid = f->vlan_id, }; + ether_addr_copy(fdb.addr, f->addr.addr); switchdev_port_obj_del(f->dst->dev, &fdb.obj); } diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 84cd863..b0b8da0 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -393,7 +393,7 @@ static int dsa_slave_port_fdb_dump(struct net_device *dev, if (ret < 0) break; - fdb->addr = addr; + ether_addr_copy(fdb->addr, addr); fdb->vid = vid; fdb->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE; diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index 007b8f4..5963d7a 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -891,10 +892,10 @@ int switchdev_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], { struct switchdev_obj_port_fdb fdb = { .obj.id = SWITCHDEV_OBJ_ID_PORT_FDB, - .addr = addr, .vid = vid, }; + ether_addr_copy(fdb.addr, addr); return switchdev_port_obj_add(dev, &fdb.obj); } EXPORT_SYMBOL_GPL(switchdev_port_fdb_add); @@ -916,10 +917,10 @@ int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], { struct switchdev_obj_port_fdb fdb = { .obj.id = SWITCHDEV_OBJ_ID_PORT_FDB, - .addr = addr, .vid = vid, }; + ether_addr_copy(fdb.addr, addr); return switchdev_port_obj_del(dev, &fdb.obj); } EXPORT_SYMBOL_GPL(switchdev_port_fdb_del); @@ -1081,7 +1082,6 @@ int switchdev_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi, .obj.id = SWITCHDEV_OBJ_ID_IPV4_FIB, .dst = dst, .dst_len = dst_len, - .fi = fi, .tos = tos, .type = type, .nlflags = nlflags, @@ -1090,6 +1090,8 @@ int switchdev_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi, struct net_device *dev; int err = 0; + memcpy(&ipv4_fib.fi, fi, sizeof(ipv4_fib.fi)); + /* Don't offload route if using custom ip rules or if * IPv4 FIB offloading has been disabled completely. */ @@ -1133,7 +1135,6 @@ int switchdev_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi, .obj.id = SWITCHDEV_OBJ_ID_IPV4_FIB, .dst = dst, .dst_len = dst_len, - .fi = fi, .tos = tos, .type = type, .nlflags = 0, @@ -1142,6 +1143,8 @@ int switchdev_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi, struct net_device *dev; int err = 0; + memcpy(&ipv4_fib.fi, fi, sizeof(ipv4_fib.fi)); + if (!(fi->fib_flags & RTNH_F_OFFLOAD)) return 0; -- cgit v0.10.2 From 4d429c5ddc5128fccd3048059ae26bb39f0d8284 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 14 Oct 2015 19:40:52 +0200 Subject: switchdev: introduce possibility to defer obj_add/del Similar to the attr usecase, the caller knows if he is holding RTNL and is in atomic section. So let the called to decide the correct call variant. This allows drivers to sleep inside their ops and wait for hw to get the operation status. Then the status is propagated into switchdev core. This avoids silent errors in drivers. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/include/net/switchdev.h b/include/net/switchdev.h index f8672d7..bc865e2 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -69,6 +69,7 @@ enum switchdev_obj_id { struct switchdev_obj { enum switchdev_obj_id id; + u32 flags; }; /* SWITCHDEV_OBJ_ID_PORT_VLAN */ diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index 5963d7a..eac68c4 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -362,21 +362,8 @@ static int __switchdev_port_obj_add(struct net_device *dev, return err; } -/** - * switchdev_port_obj_add - Add port object - * - * @dev: port device - * @id: object ID - * @obj: object to add - * - * Use a 2-phase prepare-commit transaction model to ensure - * system is not left in a partially updated state due to - * failure from driver/device. - * - * rtnl_lock must be held. - */ -int switchdev_port_obj_add(struct net_device *dev, - const struct switchdev_obj *obj) +static int switchdev_port_obj_add_now(struct net_device *dev, + const struct switchdev_obj *obj) { struct switchdev_trans trans; int err; @@ -418,18 +405,53 @@ int switchdev_port_obj_add(struct net_device *dev, return err; } -EXPORT_SYMBOL_GPL(switchdev_port_obj_add); + +static void switchdev_port_obj_add_deferred(struct net_device *dev, + const void *data) +{ + const struct switchdev_obj *obj = data; + int err; + + err = switchdev_port_obj_add_now(dev, obj); + if (err && err != -EOPNOTSUPP) + netdev_err(dev, "failed (err=%d) to add object (id=%d)\n", + err, obj->id); +} + +static int switchdev_port_obj_add_defer(struct net_device *dev, + const struct switchdev_obj *obj) +{ + return switchdev_deferred_enqueue(dev, obj, sizeof(*obj), + switchdev_port_obj_add_deferred); +} /** - * switchdev_port_obj_del - Delete port object + * switchdev_port_obj_add - Add port object * * @dev: port device * @id: object ID - * @obj: object to delete + * @obj: object to add + * + * Use a 2-phase prepare-commit transaction model to ensure + * system is not left in a partially updated state due to + * failure from driver/device. + * + * rtnl_lock must be held and must not be in atomic section, + * in case SWITCHDEV_F_DEFER flag is not set. */ -int switchdev_port_obj_del(struct net_device *dev, +int switchdev_port_obj_add(struct net_device *dev, const struct switchdev_obj *obj) { + if (obj->flags & SWITCHDEV_F_DEFER) + return switchdev_port_obj_add_defer(dev, obj); + ASSERT_RTNL(); + return switchdev_port_obj_add_now(dev, obj); +} +EXPORT_SYMBOL_GPL(switchdev_port_obj_add); + +static int switchdev_port_obj_del_now(struct net_device *dev, + const struct switchdev_obj *obj) +{ const struct switchdev_ops *ops = dev->switchdev_ops; struct net_device *lower_dev; struct list_head *iter; @@ -444,13 +466,51 @@ int switchdev_port_obj_del(struct net_device *dev, */ netdev_for_each_lower_dev(dev, lower_dev, iter) { - err = switchdev_port_obj_del(lower_dev, obj); + err = switchdev_port_obj_del_now(lower_dev, obj); if (err) break; } return err; } + +static void switchdev_port_obj_del_deferred(struct net_device *dev, + const void *data) +{ + const struct switchdev_obj *obj = data; + int err; + + err = switchdev_port_obj_del_now(dev, obj); + if (err && err != -EOPNOTSUPP) + netdev_err(dev, "failed (err=%d) to del object (id=%d)\n", + err, obj->id); +} + +static int switchdev_port_obj_del_defer(struct net_device *dev, + const struct switchdev_obj *obj) +{ + return switchdev_deferred_enqueue(dev, obj, sizeof(*obj), + switchdev_port_obj_del_deferred); +} + +/** + * switchdev_port_obj_del - Delete port object + * + * @dev: port device + * @id: object ID + * @obj: object to delete + * + * rtnl_lock must be held and must not be in atomic section, + * in case SWITCHDEV_F_DEFER flag is not set. + */ +int switchdev_port_obj_del(struct net_device *dev, + const struct switchdev_obj *obj) +{ + if (obj->flags & SWITCHDEV_F_DEFER) + return switchdev_port_obj_del_defer(dev, obj); + ASSERT_RTNL(); + return switchdev_port_obj_del_now(dev, obj); +} EXPORT_SYMBOL_GPL(switchdev_port_obj_del); /** -- cgit v0.10.2 From 56607386e80cc7ce923592e115a3492485b47c72 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 14 Oct 2015 19:40:53 +0200 Subject: bridge: defer switchdev fdb del call in fdb_del_external_learn Since spinlock is held here, defer the switchdev operation. Also, ensure that defered switchdev ops are processed before port master device is unlinked. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index f5e7da0..c88bd8e 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -134,7 +134,10 @@ static void fdb_del_hw_addr(struct net_bridge *br, const unsigned char *addr) static void fdb_del_external_learn(struct net_bridge_fdb_entry *f) { struct switchdev_obj_port_fdb fdb = { - .obj.id = SWITCHDEV_OBJ_ID_PORT_FDB, + .obj = { + .id = SWITCHDEV_OBJ_ID_PORT_FDB, + .flags = SWITCHDEV_F_DEFER, + }, .vid = f->vlan_id, }; diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 45e4757..ec02f58 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "br_private.h" @@ -250,6 +251,8 @@ static void del_nbp(struct net_bridge_port *p) nbp_vlan_flush(p); br_fdb_delete_by_port(br, p, 0, 1); + switchdev_deferred_process(); + nbp_update_port_count(br); netdev_upper_dev_unlink(dev, br->dev); -- cgit v0.10.2 From d33eeb645d59ffd14bbc6db977c3783af42dd700 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 14 Oct 2015 19:40:54 +0200 Subject: rocker: remove nowait from switchdev callbacks. No need to avoid sleeping in switchdev callbacks now, as the switchdev core allows it. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c index 2cd7435..32a80d2 100644 --- a/drivers/net/ethernet/rocker/rocker.c +++ b/drivers/net/ethernet/rocker/rocker.c @@ -3672,7 +3672,7 @@ static int rocker_port_fdb_flush(struct rocker_port *rocker_port, rocker_port->stp_state == BR_STATE_FORWARDING) return 0; - flags |= ROCKER_OP_FLAG_REMOVE; + flags |= ROCKER_OP_FLAG_NOWAIT | ROCKER_OP_FLAG_REMOVE; spin_lock_irqsave(&rocker->fdb_tbl_lock, lock_flags); @@ -4382,8 +4382,7 @@ static int rocker_port_attr_set(struct net_device *dev, switch (attr->id) { case SWITCHDEV_ATTR_ID_PORT_STP_STATE: - err = rocker_port_stp_update(rocker_port, trans, - ROCKER_OP_FLAG_NOWAIT, + err = rocker_port_stp_update(rocker_port, trans, 0, attr->u.stp_state); break; case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: @@ -4517,7 +4516,7 @@ static int rocker_port_fdb_del(struct rocker_port *rocker_port, const struct switchdev_obj_port_fdb *fdb) { __be16 vlan_id = rocker_port_vid_to_vlan(rocker_port, fdb->vid, NULL); - int flags = ROCKER_OP_FLAG_NOWAIT | ROCKER_OP_FLAG_REMOVE; + int flags = ROCKER_OP_FLAG_REMOVE; if (!rocker_port_is_bridged(rocker_port)) return -EINVAL; -- cgit v0.10.2 From 771acac2ffa5957b91e881908cd4c9657978a209 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 14 Oct 2015 19:40:55 +0200 Subject: switchdev: assert rtnl mutex when going over lower netdevs netdev_for_each_lower_dev has to be called with rtnl mutex held. So better enforce it in switchdev functions. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index eac68c4..73e3895 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -520,6 +520,8 @@ EXPORT_SYMBOL_GPL(switchdev_port_obj_del); * @id: object ID * @obj: object to dump * @cb: function to call with a filled object + * + * rtnl_lock must be held. */ int switchdev_port_obj_dump(struct net_device *dev, struct switchdev_obj *obj, switchdev_obj_dump_cb_t *cb) @@ -529,6 +531,8 @@ int switchdev_port_obj_dump(struct net_device *dev, struct switchdev_obj *obj, struct list_head *iter; int err = -EOPNOTSUPP; + ASSERT_RTNL(); + if (ops && ops->switchdev_port_obj_dump) return ops->switchdev_port_obj_dump(dev, obj, cb); @@ -1097,6 +1101,8 @@ static struct net_device *switchdev_get_dev_by_nhs(struct fib_info *fi) struct net_device *dev = NULL; int nhsel; + ASSERT_RTNL(); + /* For this route, all nexthop devs must be on the same switch. */ for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) { @@ -1327,10 +1333,11 @@ void switchdev_port_fwd_mark_set(struct net_device *dev, u32 mark = dev->ifindex; u32 reset_mark = 0; - if (group_dev && joining) { - mark = switchdev_port_fwd_mark_get(dev, group_dev); - } else if (group_dev && !joining) { - if (dev->offload_fwd_mark == mark) + if (group_dev) { + ASSERT_RTNL(); + if (joining) + mark = switchdev_port_fwd_mark_get(dev, group_dev); + else if (dev->offload_fwd_mark == mark) /* Ohoh, this port was the mark reference port, * but it's leaving the group, so reset the * mark for the remaining ports in the group. -- cgit v0.10.2 From 96aec911482246a319bf457f39fa848ce436c8fd Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Wed, 14 Oct 2015 12:37:32 -0500 Subject: amd-xgbe: Use system workqueue for device restart A previous patch switched from using the system workqueue to the device workqueue for various operations. During a device restart the device workqueue is flushed so the restart cannot use this workqueue or else a deadlock results. Move the device restart back to using the system workqueue. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index 14bad8c..49f796a 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -365,7 +365,7 @@ static irqreturn_t xgbe_isr(int irq, void *data) /* Restart the device on a Fatal Bus Error */ if (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, FBE)) - queue_work(pdata->dev_workqueue, &pdata->restart_work); + schedule_work(&pdata->restart_work); /* Clear all interrupt signals */ XGMAC_DMA_IOWRITE(channel, DMA_CH_SR, dma_ch_isr); @@ -1537,7 +1537,7 @@ static void xgbe_tx_timeout(struct net_device *netdev) struct xgbe_prv_data *pdata = netdev_priv(netdev); netdev_warn(netdev, "tx timeout, device restarting\n"); - queue_work(pdata->dev_workqueue, &pdata->restart_work); + schedule_work(&pdata->restart_work); } static struct rtnl_link_stats64 *xgbe_get_stats64(struct net_device *netdev, -- cgit v0.10.2 From 33a704a59b6618dceb4a3ec1200b879294962c88 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 15 Oct 2015 17:43:15 +0200 Subject: mlxsw: Remove multicast ID configuration With respect to a firmware change, the Switch Multicast ID (SMID) register is no longer needed, so the related configuration code can be removed. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 096e1c1..5e0c957 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -99,57 +99,6 @@ static const struct mlxsw_reg_info mlxsw_reg_spad = { */ MLXSW_ITEM_BUF(reg, spad, base_mac, 0x02, 6); -/* SMID - Switch Multicast ID - * -------------------------- - * In multi-chip configuration, each device should maintain mapping between - * Multicast ID (MID) into a list of local ports. This mapping is used in all - * the devices other than the ingress device, and is implemented as part of the - * FDB. The MID record maps from a MID, which is a unique identi- fier of the - * multicast group within the stacking domain, into a list of local ports into - * which the packet is replicated. - */ -#define MLXSW_REG_SMID_ID 0x2007 -#define MLXSW_REG_SMID_LEN 0x420 - -static const struct mlxsw_reg_info mlxsw_reg_smid = { - .id = MLXSW_REG_SMID_ID, - .len = MLXSW_REG_SMID_LEN, -}; - -/* reg_smid_swid - * Switch partition ID. - * Access: Index - */ -MLXSW_ITEM32(reg, smid, swid, 0x00, 24, 8); - -/* reg_smid_mid - * Multicast identifier - global identifier that represents the multicast group - * across all devices - * Access: Index - */ -MLXSW_ITEM32(reg, smid, mid, 0x00, 0, 16); - -/* reg_smid_port - * Local port memebership (1 bit per port). - * Access: RW - */ -MLXSW_ITEM_BIT_ARRAY(reg, smid, port, 0x20, 0x20, 1); - -/* reg_smid_port_mask - * Local port mask (1 bit per port). - * Access: W - */ -MLXSW_ITEM_BIT_ARRAY(reg, smid, port_mask, 0x220, 0x20, 1); - -static inline void mlxsw_reg_smid_pack(char *payload, u16 mid) -{ - MLXSW_REG_ZERO(smid, payload); - mlxsw_reg_smid_swid_set(payload, 0); - mlxsw_reg_smid_mid_set(payload, mid); - mlxsw_reg_smid_port_set(payload, MLXSW_PORT_CPU_PORT, 1); - mlxsw_reg_smid_port_mask_set(payload, MLXSW_PORT_CPU_PORT, 1); -} - /* SSPR - Switch System Port Record Register * ----------------------------------------- * Configures the system port to local port mapping. @@ -1272,8 +1221,6 @@ static inline const char *mlxsw_reg_id_str(u16 reg_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_SPMS_ID: diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index d448431..835b632 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -1371,20 +1371,9 @@ static int mlxsw_sx_flood_init(struct mlxsw_sx *mlxsw_sx) { char sfgc_pl[MLXSW_REG_SFGC_LEN]; char sgcr_pl[MLXSW_REG_SGCR_LEN]; - char *smid_pl; char *sftr_pl; int err; - /* Due to FW bug, we must configure SMID. */ - smid_pl = kmalloc(MLXSW_REG_SMID_LEN, GFP_KERNEL); - if (!smid_pl) - return -ENOMEM; - mlxsw_reg_smid_pack(smid_pl, MLXSW_PORT_MID); - err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(smid), smid_pl); - kfree(smid_pl); - if (err) - return err; - /* Configure a flooding table, which includes only CPU port. */ sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL); if (!sftr_pl) -- cgit v0.10.2 From ffe053285b77e9cd87c3b8e37f35bdb8de8172d1 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 15 Oct 2015 17:43:16 +0200 Subject: mlxsw: switchx2: Use ETH_ALEN for mac address length Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 835b632..f057d43 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -57,13 +57,11 @@ static const char mlxsw_sx_driver_version[] = "1.0"; struct mlxsw_sx_port; -#define MLXSW_SW_HW_ID_LEN 6 - struct mlxsw_sx { struct mlxsw_sx_port **ports; struct mlxsw_core *core; const struct mlxsw_bus_info *bus_info; - u8 hw_id[MLXSW_SW_HW_ID_LEN]; + u8 hw_id[ETH_ALEN]; }; struct mlxsw_sx_port_pcpu_stats { -- cgit v0.10.2 From e4c870b1b496c90dc4fa9cf24937c7a45e84187f Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 15 Oct 2015 17:43:17 +0200 Subject: mlxsw: pci: Use MLXSW_PCI_CQS_MAX instead of MLXSW_PCI_CQS_COUNT The count of CQs can be different for various ASICs, so just define maximal value and check for that. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c index 462cea3..5ba740a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c @@ -830,7 +830,8 @@ static void mlxsw_pci_eq_tasklet(unsigned long data) { struct mlxsw_pci_queue *q = (struct mlxsw_pci_queue *) data; struct mlxsw_pci *mlxsw_pci = q->pci; - unsigned long active_cqns[BITS_TO_LONGS(MLXSW_PCI_CQS_COUNT)]; + u8 cq_count = mlxsw_pci_cq_count(mlxsw_pci); + unsigned long active_cqns[BITS_TO_LONGS(MLXSW_PCI_CQS_MAX)]; char *eqe; u8 cqn; bool cq_handle = false; @@ -866,7 +867,7 @@ static void mlxsw_pci_eq_tasklet(unsigned long data) if (!cq_handle) return; - for_each_set_bit(cqn, active_cqns, MLXSW_PCI_CQS_COUNT) { + for_each_set_bit(cqn, active_cqns, cq_count) { q = mlxsw_pci_cq_get(mlxsw_pci, cqn); mlxsw_pci_queue_tasklet_schedule(q); } @@ -1069,8 +1070,7 @@ static int mlxsw_pci_aqs_init(struct mlxsw_pci *mlxsw_pci, char *mbox) if ((num_sdqs != MLXSW_PCI_SDQS_COUNT) || (num_rdqs != MLXSW_PCI_RDQS_COUNT) || - (num_cqs != MLXSW_PCI_CQS_COUNT) || - (num_eqs != MLXSW_PCI_EQS_COUNT)) { + num_cqs > MLXSW_PCI_CQS_MAX || num_eqs != MLXSW_PCI_EQS_COUNT) { dev_err(&pdev->dev, "Unsupported number of queues\n"); return -EINVAL; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.h b/drivers/net/ethernet/mellanox/mlxsw/pci.h index 1ef9664..efe1b4b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.h +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.h @@ -73,7 +73,7 @@ #define MLXSW_PCI_RDQS_COUNT 24 #define MLXSW_PCI_SDQS_COUNT 24 -#define MLXSW_PCI_CQS_COUNT (MLXSW_PCI_RDQS_COUNT + MLXSW_PCI_SDQS_COUNT) +#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 -- cgit v0.10.2 From 424e1114af94a543b3126f84ddd38742c7cfbbb6 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 15 Oct 2015 17:43:18 +0200 Subject: mlxsw: pci: Do not use MLXSW_PCI_SDQS_COUNT define Use mlxsw_pci_sdq_count helper instead. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c index 5ba740a..946341e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c @@ -497,6 +497,7 @@ static int mlxsw_pci_rdq_init(struct mlxsw_pci *mlxsw_pci, char *mbox, struct mlxsw_pci_queue *q) { struct mlxsw_pci_queue_elem_info *elem_info; + u8 sdq_count = mlxsw_pci_sdq_count(mlxsw_pci); int i; int err; @@ -504,9 +505,9 @@ static int mlxsw_pci_rdq_init(struct mlxsw_pci *mlxsw_pci, char *mbox, q->consumer_counter = 0; /* Set CQ of same number of this RDQ with base - * above MLXSW_PCI_SDQS_MAX as the lower ones are assigned to SDQs. + * above SDQ count as the lower ones are assigned to SDQs. */ - mlxsw_cmd_mbox_sw2hw_dq_cq_set(mbox, q->num + MLXSW_PCI_SDQS_COUNT); + mlxsw_cmd_mbox_sw2hw_dq_cq_set(mbox, sdq_count + q->num); mlxsw_cmd_mbox_sw2hw_dq_log2_dq_sz_set(mbox, 3); /* 8 pages */ for (i = 0; i < MLXSW_PCI_AQ_PAGES; i++) { dma_addr_t mapaddr = __mlxsw_pci_queue_page_get(q, i); -- cgit v0.10.2 From c85c3882ad88dd36105148912cd95084b09a5676 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 15 Oct 2015 17:43:19 +0200 Subject: mlxsw: pci: Remove MLXSW_PCI_RDQS/SDQS defines and checks Remove strict number check of queues count as various ASICs have different counts. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c index 946341e..870d443 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c @@ -1069,8 +1069,7 @@ static int mlxsw_pci_aqs_init(struct mlxsw_pci *mlxsw_pci, char *mbox) num_eqs = mlxsw_cmd_mbox_query_aq_cap_max_num_eqs_get(mbox); eq_log2sz = mlxsw_cmd_mbox_query_aq_cap_log_max_eq_sz_get(mbox); - if ((num_sdqs != MLXSW_PCI_SDQS_COUNT) || - (num_rdqs != MLXSW_PCI_RDQS_COUNT) || + if (num_sdqs + num_rdqs > num_cqs || num_cqs > MLXSW_PCI_CQS_MAX || num_eqs != MLXSW_PCI_EQS_COUNT) { dev_err(&pdev->dev, "Unsupported number of queues\n"); return -EINVAL; diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.h b/drivers/net/ethernet/mellanox/mlxsw/pci.h index efe1b4b..5b3453b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.h +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.h @@ -71,8 +71,6 @@ #define MLXSW_PCI_DOORBELL(offset, type_offset, num) \ ((offset) + (type_offset) + (num) * 4) -#define MLXSW_PCI_RDQS_COUNT 24 -#define MLXSW_PCI_SDQS_COUNT 24 #define MLXSW_PCI_CQS_MAX 96 #define MLXSW_PCI_EQS_COUNT 2 #define MLXSW_PCI_EQ_ASYNC_NUM 0 -- cgit v0.10.2 From 3e2206da73e37c0fc07675b5caf25d3b26e33b7d Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 15 Oct 2015 17:43:20 +0200 Subject: mlxsw: pci: Limit number of entries being sent in single MAP_FA cmd Firmware accepts only limited number of mapping entries for MAP_FA command. In order to prevent overflow, introduce a limit and in case the number of entries is bigger, call MAP_FA multiple times. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/cmd.h b/drivers/net/ethernet/mellanox/mlxsw/cmd.h index 770db17..27e48b4 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/cmd.h +++ b/drivers/net/ethernet/mellanox/mlxsw/cmd.h @@ -464,6 +464,8 @@ MLXSW_ITEM32(cmd_mbox, query_aq_cap, max_sg_rq, 0x10, 0, 8); * passed in this command must be pinned. */ +#define MLXSW_CMD_MAP_FA_VPM_ENTRIES_MAX 32 + static inline int mlxsw_cmd_map_fa(struct mlxsw_core *mlxsw_core, char *in_mbox, u32 vpm_entries_count) { diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c index 870d443..87afa5f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c @@ -171,8 +171,8 @@ struct mlxsw_pci { struct msix_entry msix_entry; struct mlxsw_core *core; struct { - u16 num_pages; struct mlxsw_pci_mem_item *items; + unsigned int count; } fw_area; struct { struct mlxsw_pci_mem_item out_mbox; @@ -1272,6 +1272,7 @@ static int mlxsw_pci_fw_area_init(struct mlxsw_pci *mlxsw_pci, char *mbox, u16 num_pages) { struct mlxsw_pci_mem_item *mem_item; + int nent = 0; int i; int err; @@ -1279,7 +1280,7 @@ static int mlxsw_pci_fw_area_init(struct mlxsw_pci *mlxsw_pci, char *mbox, GFP_KERNEL); if (!mlxsw_pci->fw_area.items) return -ENOMEM; - mlxsw_pci->fw_area.num_pages = num_pages; + mlxsw_pci->fw_area.count = num_pages; mlxsw_cmd_mbox_zero(mbox); for (i = 0; i < num_pages; i++) { @@ -1293,13 +1294,22 @@ static int mlxsw_pci_fw_area_init(struct mlxsw_pci *mlxsw_pci, char *mbox, err = -ENOMEM; goto err_alloc; } - mlxsw_cmd_mbox_map_fa_pa_set(mbox, i, mem_item->mapaddr); - mlxsw_cmd_mbox_map_fa_log2size_set(mbox, i, 0); /* 1 page */ + mlxsw_cmd_mbox_map_fa_pa_set(mbox, nent, mem_item->mapaddr); + mlxsw_cmd_mbox_map_fa_log2size_set(mbox, nent, 0); /* 1 page */ + if (++nent == MLXSW_CMD_MAP_FA_VPM_ENTRIES_MAX) { + err = mlxsw_cmd_map_fa(mlxsw_pci->core, mbox, nent); + if (err) + goto err_cmd_map_fa; + nent = 0; + mlxsw_cmd_mbox_zero(mbox); + } } - err = mlxsw_cmd_map_fa(mlxsw_pci->core, mbox, num_pages); - if (err) - goto err_cmd_map_fa; + if (nent) { + err = mlxsw_cmd_map_fa(mlxsw_pci->core, mbox, nent); + if (err) + goto err_cmd_map_fa; + } return 0; @@ -1322,7 +1332,7 @@ static void mlxsw_pci_fw_area_fini(struct mlxsw_pci *mlxsw_pci) mlxsw_cmd_unmap_fa(mlxsw_pci->core); - for (i = 0; i < mlxsw_pci->fw_area.num_pages; i++) { + for (i = 0; i < mlxsw_pci->fw_area.count; i++) { mem_item = &mlxsw_pci->fw_area.items[i]; pci_free_consistent(mlxsw_pci->pdev, mem_item->size, -- cgit v0.10.2 From 18ea54454ecdd516ad11bf88d86f1994548692bf Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 15 Oct 2015 17:43:21 +0200 Subject: mlxsw: core: Do not use EMADs in mlxsw_emad_fini Be symmetric with mlxsw_emad_init and don't use EMADs in mlxsw_emad_fini cleanup function. Use command interface instead. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index dbcaf5d..7bf51d1 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -551,6 +551,7 @@ static void mlxsw_emad_fini(struct mlxsw_core *mlxsw_core) { char hpkt_pl[MLXSW_REG_HPKT_LEN]; + mlxsw_core->emad.use_emad = false; mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_DISCARD, MLXSW_REG_HTGT_TRAP_GROUP_EMAD, MLXSW_TRAP_ID_ETHEMAD); -- cgit v0.10.2 From 6cf9dc8b77918c30260bc0f2e1853eb7d1ce34e8 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 15 Oct 2015 17:43:22 +0200 Subject: mlxsw: Use dev_level_ratelimited instead of net_ratelimit & dev_level Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c index 87afa5f..974ce47 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c @@ -431,8 +431,7 @@ static int mlxsw_pci_wqe_frag_map(struct mlxsw_pci *mlxsw_pci, char *wqe, mapaddr = pci_map_single(pdev, frag_data, frag_len, direction); if (unlikely(pci_dma_mapping_error(pdev, mapaddr))) { - if (net_ratelimit()) - dev_err(&pdev->dev, "failed to dma map tx frag\n"); + dev_err_ratelimited(&pdev->dev, "failed to dma map tx frag\n"); return -EIO; } mlxsw_pci_wqe_address_set(wqe, index, mapaddr); @@ -700,8 +699,8 @@ static void mlxsw_pci_cqe_rdq_handle(struct mlxsw_pci *mlxsw_pci, put_new_skb: memset(wqe, 0, q->elem_size); err = mlxsw_pci_rdq_skb_alloc(mlxsw_pci, elem_info); - if (err && net_ratelimit()) - dev_dbg(&pdev->dev, "Failed to alloc skb for RDQ\n"); + if (err) + dev_dbg_ratelimited(&pdev->dev, "Failed to alloc skb for RDQ\n"); /* Everything is set up, ring doorbell to pass elem to HW */ q->producer_counter++; mlxsw_pci_queue_doorbell_producer_ring(mlxsw_pci, q); diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index f057d43..698b182 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -1210,9 +1210,8 @@ static void mlxsw_sx_rx_listener_func(struct sk_buff *skb, u8 local_port, struct mlxsw_sx_port_pcpu_stats *pcpu_stats; if (unlikely(!mlxsw_sx_port)) { - if (net_ratelimit()) - dev_warn(mlxsw_sx->bus_info->dev, "Port %d: skb received for non-existent port\n", - local_port); + dev_warn_ratelimited(mlxsw_sx->bus_info->dev, "Port %d: skb received for non-existent port\n", + local_port); return; } -- cgit v0.10.2 From 3f0effd16b69d84558f2e7eb7ccd0e27a43173f4 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 15 Oct 2015 17:43:23 +0200 Subject: mlxsw: reg: Uppercase letters in register IDs Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 5e0c957..a2ebb79 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -161,7 +161,7 @@ static inline void mlxsw_reg_sspr_pack(char *payload, u8 local_port) * ------------------------------------------- * Configures the spanning tree state of a physical port. */ -#define MLXSW_REG_SPMS_ID 0x200d +#define MLXSW_REG_SPMS_ID 0x200D #define MLXSW_REG_SPMS_LEN 0x404 static const struct mlxsw_reg_info mlxsw_reg_spms = { @@ -962,7 +962,7 @@ static inline void mlxsw_reg_ppcnt_pack(char *payload, u8 local_port) * Controls the association of a port with a switch partition and enables * configuring ports as stacking ports. */ -#define MLXSW_REG_PSPA_ID 0x500d +#define MLXSW_REG_PSPA_ID 0x500D #define MLXSW_REG_PSPA_LEN 0x8 static const struct mlxsw_reg_info mlxsw_reg_pspa = { -- cgit v0.10.2 From 36b78e8ababe6c596680d742f20bece5e8dc9820 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 15 Oct 2015 17:43:24 +0200 Subject: mlxsw: reg: Remove extra space in SFGC ID define Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index a2ebb79..34b95a1 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -205,7 +205,7 @@ static inline void mlxsw_reg_spms_pack(char *payload, u8 local_port, u16 vid, * The following register controls the association of flooding tables and MIDs * to packet types used for flooding. */ -#define MLXSW_REG_SFGC_ID 0x2011 +#define MLXSW_REG_SFGC_ID 0x2011 #define MLXSW_REG_SFGC_LEN 0x10 static const struct mlxsw_reg_info mlxsw_reg_sfgc = { -- cgit v0.10.2 From fa6ad058bc77b6ae69bd4512b538245ee5db39eb Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 15 Oct 2015 17:43:25 +0200 Subject: mlxsw: reg: Adjust definition of enum mlxsw_reg_sfgc_type Define max which would be needed later on. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 34b95a1..6fca13a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -214,13 +214,15 @@ static const struct mlxsw_reg_info mlxsw_reg_sfgc = { }; enum mlxsw_reg_sfgc_type { - MLXSW_REG_SFGC_TYPE_BROADCAST = 0, - MLXSW_REG_SFGC_TYPE_UNKNOWN_UNICAST = 1, - MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV4 = 2, - MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV6 = 3, - MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_NON_IP = 5, - MLXSW_REG_SFGC_TYPE_IPV4_LINK_LOCAL = 6, - MLXSW_REG_SFGC_TYPE_IPV6_ALL_HOST = 7, + MLXSW_REG_SFGC_TYPE_BROADCAST, + MLXSW_REG_SFGC_TYPE_UNKNOWN_UNICAST, + MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV4, + MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV6, + MLXSW_REG_SFGC_TYPE_RESERVED, + MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_NON_IP, + MLXSW_REG_SFGC_TYPE_IPV4_LINK_LOCAL, + MLXSW_REG_SFGC_TYPE_IPV6_ALL_HOST, + MLXSW_REG_SFGC_TYPE_MAX, }; /* reg_sfgc_type -- cgit v0.10.2 From ebb7963f9b14bbaa07b6074579225d6338318ecb Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 15 Oct 2015 17:43:26 +0200 Subject: mlxsw: Introduce mlxsw_reg_spms_vid_pack helper and use it Introduce separate helper for packing SPMS VIDs, as it can be used for multiple VIDs and not only for one as previous SPMS pack function provided. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 6fca13a..7595cf1 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -192,11 +192,15 @@ enum mlxsw_reg_spms_state { */ MLXSW_ITEM_BIT_ARRAY(reg, spms, state, 0x04, 0x400, 2); -static inline void mlxsw_reg_spms_pack(char *payload, u8 local_port, u16 vid, - enum mlxsw_reg_spms_state state) +static inline void mlxsw_reg_spms_pack(char *payload, u8 local_port) { MLXSW_REG_ZERO(spms, payload); mlxsw_reg_spms_local_port_set(payload, local_port); +} + +static inline void mlxsw_reg_spms_vid_pack(char *payload, u16 vid, + enum mlxsw_reg_spms_state state) +{ mlxsw_reg_spms_state_set(payload, vid, state); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 698b182..7d5c851 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -923,7 +923,8 @@ static int mlxsw_sx_port_stp_state_set(struct mlxsw_sx_port *mlxsw_sx_port, spms_pl = kmalloc(MLXSW_REG_SPMS_LEN, GFP_KERNEL); if (!spms_pl) return -ENOMEM; - mlxsw_reg_spms_pack(spms_pl, mlxsw_sx_port->local_port, vid, state); + mlxsw_reg_spms_pack(spms_pl, mlxsw_sx_port->local_port); + mlxsw_reg_spms_vid_pack(spms_pl, vid, state); err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(spms), spms_pl); kfree(spms_pl); return err; -- cgit v0.10.2 From f24af330159aa1afbc6670e5786856a2a99d112c Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 15 Oct 2015 17:43:27 +0200 Subject: mlxsw: Simplify traps creation The Host Trap Group Table (HTGT) register configures trap groups, which are populated with trap IDs using the Host PacKet Trap (HPKT) register. However, a trap ID can only be present inside one trap group (the last configured). Instead of passing both the trap group and ID for the function that packs HPKT, pass only the trap ID and derive from it the trap group. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index 7bf51d1..9f4a0bf 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -506,7 +506,6 @@ 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_REG_HTGT_TRAP_GROUP_EMAD, MLXSW_TRAP_ID_ETHEMAD); return mlxsw_reg_write(mlxsw_core, MLXSW_REG(hpkt), hpkt_pl); } @@ -553,7 +552,6 @@ 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_REG_HTGT_TRAP_GROUP_EMAD, MLXSW_TRAP_ID_ETHEMAD); mlxsw_reg_write(mlxsw_core, MLXSW_REG(hpkt), hpkt_pl); diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 7595cf1..fc6f520 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -1209,12 +1209,22 @@ enum { */ MLXSW_ITEM32(reg, hpkt, ctrl, 0x04, 16, 2); -static inline void mlxsw_reg_hpkt_pack(char *payload, u8 action, - u8 trap_group, u16 trap_id) +static inline void mlxsw_reg_hpkt_pack(char *payload, u8 action, u16 trap_id) { + u8 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); diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 7d5c851..85dcda4 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -1177,8 +1177,7 @@ static int mlxsw_sx_event_register(struct mlxsw_sx *mlxsw_sx, if (err) return err; - mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD, - MLXSW_REG_HTGT_TRAP_GROUP_EMAD, trap_id); + 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; @@ -1322,7 +1321,6 @@ static int mlxsw_sx_traps_init(struct mlxsw_sx *mlxsw_sx) goto err_rx_listener_register; mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_TRAP_TO_CPU, - MLXSW_REG_HTGT_TRAP_GROUP_RX, mlxsw_sx_rx_listener[i].trap_id); err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(hpkt), hpkt_pl); if (err) @@ -1337,7 +1335,6 @@ err_rx_trap_set: err_rx_listener_register: for (i--; i >= 0; i--) { mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD, - MLXSW_REG_HTGT_TRAP_GROUP_RX, mlxsw_sx_rx_listener[i].trap_id); mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(hpkt), hpkt_pl); @@ -1355,7 +1352,6 @@ static void mlxsw_sx_traps_fini(struct mlxsw_sx *mlxsw_sx) for (i = 0; i < ARRAY_SIZE(mlxsw_sx_rx_listener); i++) { mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD, - MLXSW_REG_HTGT_TRAP_GROUP_RX, mlxsw_sx_rx_listener[i].trap_id); mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(hpkt), hpkt_pl); -- cgit v0.10.2 From 801bd3defb2eac756e6570168d0f8187781ad8ad Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 15 Oct 2015 17:43:28 +0200 Subject: mlxsw: Add trap group for control packets Previously, we trapped flooded and control packets using the same trap group. This can cause flooded packets to overflow the PCI bus and prevent control packets (e.g. STP, LACP) from getting to the CPU. Solve this by splitting the RX trap group to RX and control, which allows us to configure a policer on the first, thereby preventing it from overflowing the PCI bus. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index fc6f520..7b245af 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -1029,8 +1029,11 @@ MLXSW_ITEM32(reg, htgt, swid, 0x00, 24, 8); */ MLXSW_ITEM32(reg, htgt, type, 0x00, 8, 4); -#define MLXSW_REG_HTGT_TRAP_GROUP_EMAD 0x0 -#define MLXSW_REG_HTGT_TRAP_GROUP_RX 0x1 +enum mlxsw_reg_htgt_trap_group { + MLXSW_REG_HTGT_TRAP_GROUP_EMAD, + MLXSW_REG_HTGT_TRAP_GROUP_RX, + MLXSW_REG_HTGT_TRAP_GROUP_CTRL, +}; /* reg_htgt_trap_group * Trap group number. User defined number specifying which trap groups @@ -1097,6 +1100,7 @@ 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 /* reg_htgt_local_path_rdq * Receive descriptor queue (RDQ) to use for the trap group. @@ -1104,21 +1108,29 @@ MLXSW_ITEM32(reg, htgt, local_path_cpu_tclass, 0x10, 16, 6); */ MLXSW_ITEM32(reg, htgt, local_path_rdq, 0x10, 0, 6); -static inline void mlxsw_reg_htgt_pack(char *payload, u8 trap_group) +static inline void mlxsw_reg_htgt_pack(char *payload, + enum mlxsw_reg_htgt_trap_group group) { u8 swid, rdq; MLXSW_REG_ZERO(htgt, payload); - if (MLXSW_REG_HTGT_TRAP_GROUP_EMAD == trap_group) { + switch (group) { + case MLXSW_REG_HTGT_TRAP_GROUP_EMAD: swid = MLXSW_PORT_SWID_ALL_SWIDS; rdq = MLXSW_REG_HTGT_LOCAL_PATH_RDQ_EMAD; - } else { + 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; } 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, trap_group); + 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); @@ -1211,7 +1223,7 @@ MLXSW_ITEM32(reg, hpkt, ctrl, 0x04, 16, 2); static inline void mlxsw_reg_hpkt_pack(char *payload, u8 action, u16 trap_id) { - u8 trap_group; + 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); diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 85dcda4..4f72e0a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -1313,6 +1313,11 @@ 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); + err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(htgt), htgt_pl); + if (err) + 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], -- cgit v0.10.2 From 5cd16d8c78fd17520fff437256f0c3a4e960fd5d Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 15 Oct 2015 17:43:29 +0200 Subject: mlxsw: cmd: Update CONFIG_PROFILE command documentation The meaning of certain parameters in the profile passed to the device during initialization has changed, so update their documentation accordingly. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/cmd.h b/drivers/net/ethernet/mellanox/mlxsw/cmd.h index 27e48b4..c7889f4 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/cmd.h +++ b/drivers/net/ethernet/mellanox/mlxsw/cmd.h @@ -570,7 +570,7 @@ MLXSW_ITEM32(cmd_mbox, config_profile, set_max_vlan_groups, 0x0C, 6, 1); */ MLXSW_ITEM32(cmd_mbox, config_profile, set_max_regions, 0x0C, 7, 1); -/* cmd_mbox_config_profile_set_fid_based +/* cmd_mbox_config_profile_set_flood_mode * Capability bit. Setting a bit to 1 configures the profile * according to the mailbox contents. */ @@ -651,12 +651,8 @@ MLXSW_ITEM32(cmd_mbox, config_profile, max_vlan_groups, 0x28, 0, 12); MLXSW_ITEM32(cmd_mbox, config_profile, max_regions, 0x2C, 0, 16); /* cmd_mbox_config_profile_max_flood_tables - * Maximum number of Flooding Tables. Flooding Tables are associated to - * the different packet types for the different switch partitions. - * Note that the table size depends on the fid_based mode. - * In SwitchX silicon, tables are split equally between the switch - * partitions. e.g. for 2 swids and 8 tables, the first 4 are associated - * with swid-1 and the last 4 are associated with swid-2. + * Maximum number of single-entry flooding tables. Different flooding tables + * can be associated with different packet types. */ MLXSW_ITEM32(cmd_mbox, config_profile, max_flood_tables, 0x30, 16, 4); @@ -667,12 +663,14 @@ MLXSW_ITEM32(cmd_mbox, config_profile, max_flood_tables, 0x30, 16, 4); */ MLXSW_ITEM32(cmd_mbox, config_profile, max_vid_flood_tables, 0x30, 8, 4); -/* cmd_mbox_config_profile_fid_based - * FID Based Flood Mode - * 00 Do not use FID to offset the index into the Port Group Table/Multicast ID - * 01 Use FID to offset the index to the Port Group Table (pgi) - * 10 Use FID to offset the index to the Port Group Table (pgi) and - * the Multicast ID +/* cmd_mbox_config_profile_flood_mode + * Flooding mode to use. + * 0-2 - Backward compatible modes for SwitchX devices. + * 3 - Mixed mode, where: + * max_flood_tables indicates the number of single-entry tables. + * max_vid_flood_tables indicates the number of per-VID tables. + * max_fid_offset_flood_tables indicates the number of FID-offset tables. + * max_fid_flood_tables indicates the number of per-FID tables. */ MLXSW_ITEM32(cmd_mbox, config_profile, flood_mode, 0x30, 0, 2); -- cgit v0.10.2 From e87eb4051efe76b35d0a297db772f5964a001544 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 15 Oct 2015 09:22:11 -0700 Subject: bonding: support encapsulated ipv6 TSO If using a sixtofour device on top of a bonding device, skb segmentation of TCP traffic is done right before calling bonding xmit, because bonding only enables TSO for IPv4. This patch improves single flow performance by about 120 % on my hosts, because segmentation is deferred right before calling slave xmit. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 90f2615..d0f23cd 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1071,7 +1071,7 @@ static netdev_features_t bond_fix_features(struct net_device *dev, NETIF_F_HIGHDMA | NETIF_F_LRO) #define BOND_ENC_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | NETIF_F_RXCSUM |\ - NETIF_F_TSO) + NETIF_F_ALL_TSO) static void bond_compute_features(struct bonding *bond) { -- cgit v0.10.2 From 175f8d6746aa4b1dac5ccf576bcbc5488ff21a36 Mon Sep 17 00:00:00 2001 From: Insu Yun Date: Thu, 15 Oct 2015 12:24:09 -0400 Subject: mlx4: corretly check failed allocation When allocation fails, mlx4_alloc_cmd_mailbox returns -ENOMEM. Since there is no case that mlx4_alloc_cmd_mailbox returns NULL, it needs to be checked by IS_ERR, not IS_ERR_OR_NULL Signed-off-by: Insu Yun Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/mr.c b/drivers/net/ethernet/mellanox/mlx4/mr.c index 78f51e1..9319519 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mr.c +++ b/drivers/net/ethernet/mellanox/mlx4/mr.c @@ -318,7 +318,7 @@ int mlx4_mr_hw_get_mpt(struct mlx4_dev *dev, struct mlx4_mr *mmr, key, NULL); } else { mailbox = mlx4_alloc_cmd_mailbox(dev); - if (IS_ERR_OR_NULL(mailbox)) + if (IS_ERR(mailbox)) return PTR_ERR(mailbox); err = mlx4_cmd_box(dev, 0, mailbox->dma, key, -- cgit v0.10.2 From 9945e8043ef9273cfb633d930e2a5a9116009b09 Mon Sep 17 00:00:00 2001 From: Jon Paul Maloy Date: Thu, 15 Oct 2015 14:52:40 -0400 Subject: tipc: limit usage of temporary skb list during packet reception During packet reception, the function tipc_link_rcv() adds its accepted packets to a temporary buffer queue, before finally splicing this queue into the lock protected input queue that will be delivered up to the socket layer. The purpose is to reduce potential contention on the input queue lock. However, since the vast majority of packets arrive in sequence, they will anyway be added one by one to the input queue, and the use of the temporary queue becomes a sub-optimization. The only case where this queue makes sense is when unpacking buffers from a bundle packet; here we want to avoid dozens of small buffers to be added individually to the lock-protected input queue in a tight loop. In this commit, we remove the general usage of the temporary queue, and keep it only for the packet unbundling case. Signed-off-by: Jon Maloy Acked-by: Ying Xue Signed-off-by: David S. Miller diff --git a/net/tipc/link.c b/net/tipc/link.c index 75db07c..11f7429 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -953,7 +953,7 @@ static bool tipc_data_input(struct tipc_link *link, struct sk_buff *skb, case TIPC_HIGH_IMPORTANCE: case TIPC_CRITICAL_IMPORTANCE: case CONN_MANAGER: - __skb_queue_tail(inputq, skb); + skb_queue_tail(inputq, skb); return true; case NAME_DISTRIBUTOR: node->bclink.recv_permitted = true; @@ -982,6 +982,7 @@ static int tipc_link_input(struct tipc_link *l, struct sk_buff *skb, struct tipc_msg *hdr = buf_msg(skb); struct sk_buff **reasm_skb = &l->reasm_buf; struct sk_buff *iskb; + struct sk_buff_head tmpq; int usr = msg_user(hdr); int rc = 0; int pos = 0; @@ -1006,10 +1007,12 @@ static int tipc_link_input(struct tipc_link *l, struct sk_buff *skb, } if (usr == MSG_BUNDLER) { + skb_queue_head_init(&tmpq); l->stats.recv_bundles++; l->stats.recv_bundled += msg_msgcnt(hdr); while (tipc_msg_extract(skb, &iskb, &pos)) - tipc_data_input(l, iskb, inputq); + tipc_data_input(l, iskb, &tmpq); + tipc_skb_queue_splice_tail(&tmpq, inputq); return 0; } else if (usr == MSG_FRAGMENTER) { l->stats.recv_fragments++; @@ -1053,13 +1056,10 @@ int tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb, struct sk_buff_head *xmitq) { struct sk_buff_head *arrvq = &l->deferdq; - struct sk_buff_head tmpq; struct tipc_msg *hdr; u16 seqno, rcv_nxt; int rc = 0; - __skb_queue_head_init(&tmpq); - if (unlikely(!__tipc_skb_queue_sorted(arrvq, skb))) { if (!(skb_queue_len(arrvq) % TIPC_NACK_INTV)) tipc_link_build_proto_msg(l, STATE_MSG, 0, @@ -1114,8 +1114,8 @@ int tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb, /* Packet can be delivered */ l->rcv_nxt++; l->stats.recv_info++; - if (unlikely(!tipc_data_input(l, skb, &tmpq))) - rc = tipc_link_input(l, skb, &tmpq); + if (unlikely(!tipc_data_input(l, skb, l->inputq))) + rc = tipc_link_input(l, skb, l->inputq); /* Ack at regular intervals */ if (unlikely(++l->rcv_unacked >= TIPC_MIN_LINK_WIN)) { @@ -1126,7 +1126,6 @@ int tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb, } } exit: - tipc_skb_queue_splice_tail(&tmpq, l->inputq); return rc; } -- cgit v0.10.2 From f9aa358a8109f9f33e96c3a7efb9a07631670294 Mon Sep 17 00:00:00 2001 From: Jon Paul Maloy Date: Thu, 15 Oct 2015 14:52:41 -0400 Subject: tipc: simplify tipc_link_rcv() reception loop Currently, all packets received in tipc_link_rcv() are unconditionally added to the packet deferred queue, whereafter that queue is walked and all its buffers evaluated for delivery. This is both non-optimal and and makes the queue sorting function unnecessary complex. This commit changes the loop so that an arrived packet is evaluated first, and added to the deferred queue only when a sequence number gap is discovered. A non-empty deferred queue is walked until it is empty or until its head's sequence number doesn't fit. Signed-off-by: Jon Maloy Acked-by: Ying Xue Signed-off-by: David S. Miller diff --git a/net/tipc/link.c b/net/tipc/link.c index 11f7429..8e23ab5 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -1047,44 +1047,55 @@ static bool tipc_link_release_pkts(struct tipc_link *l, u16 acked) return released; } +/* tipc_link_build_ack_msg: prepare link acknowledge message for transmission + */ +void tipc_link_build_ack_msg(struct tipc_link *l, struct sk_buff_head *xmitq) +{ + l->rcv_unacked = 0; + l->stats.sent_acks++; + tipc_link_build_proto_msg(l, STATE_MSG, 0, 0, 0, 0, xmitq); +} + +/* tipc_link_build_nack_msg: prepare link nack message for transmission + */ +static void tipc_link_build_nack_msg(struct tipc_link *l, + struct sk_buff_head *xmitq) +{ + u32 def_cnt = ++l->stats.deferred_recv; + + if ((skb_queue_len(&l->deferdq) == 1) || !(def_cnt % TIPC_NACK_INTV)) + tipc_link_build_proto_msg(l, STATE_MSG, 0, 0, 0, 0, xmitq); +} + /* tipc_link_rcv - process TIPC packets/messages arriving from off-node - * @link: the link that should handle the message + * @l: the link that should handle the message * @skb: TIPC packet * @xmitq: queue to place packets to be sent after this call */ int tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb, struct sk_buff_head *xmitq) { - struct sk_buff_head *arrvq = &l->deferdq; + struct sk_buff_head *defq = &l->deferdq; struct tipc_msg *hdr; u16 seqno, rcv_nxt; int rc = 0; - if (unlikely(!__tipc_skb_queue_sorted(arrvq, skb))) { - if (!(skb_queue_len(arrvq) % TIPC_NACK_INTV)) - tipc_link_build_proto_msg(l, STATE_MSG, 0, - 0, 0, 0, xmitq); - return rc; - } - - while ((skb = skb_peek(arrvq))) { + do { hdr = buf_msg(skb); + seqno = msg_seqno(hdr); + rcv_nxt = l->rcv_nxt; /* Verify and update link state */ - if (unlikely(msg_user(hdr) == LINK_PROTOCOL)) { - __skb_dequeue(arrvq); - rc = tipc_link_proto_rcv(l, skb, xmitq); - continue; - } + if (unlikely(msg_user(hdr) == LINK_PROTOCOL)) + return tipc_link_proto_rcv(l, skb, xmitq); if (unlikely(!link_is_up(l))) { rc = tipc_link_fsm_evt(l, LINK_ESTABLISH_EVT); - if (!link_is_up(l)) { - kfree_skb(__skb_dequeue(arrvq)); - goto exit; - } + if (!link_is_up(l)) + goto drop; } + /* Don't send probe at next timeout expiration */ l->silent_intv_cnt = 0; /* Forward queues and wake up waiting users */ @@ -1095,37 +1106,36 @@ int tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb, } /* Defer reception if there is a gap in the sequence */ - seqno = msg_seqno(hdr); - rcv_nxt = l->rcv_nxt; if (unlikely(less(rcv_nxt, seqno))) { - l->stats.deferred_recv++; - goto exit; + __tipc_skb_queue_sorted(defq, skb); + tipc_link_build_nack_msg(l, xmitq); + break; } - __skb_dequeue(arrvq); - /* Drop if packet already received */ if (unlikely(more(rcv_nxt, seqno))) { l->stats.duplicates++; - kfree_skb(skb); - goto exit; + goto drop; } /* Packet can be delivered */ l->rcv_nxt++; l->stats.recv_info++; - if (unlikely(!tipc_data_input(l, skb, l->inputq))) + + if (!tipc_data_input(l, skb, l->inputq)) rc = tipc_link_input(l, skb, l->inputq); + if (rc) + break; /* Ack at regular intervals */ - if (unlikely(++l->rcv_unacked >= TIPC_MIN_LINK_WIN)) { - l->rcv_unacked = 0; - l->stats.sent_acks++; - tipc_link_build_proto_msg(l, STATE_MSG, - 0, 0, 0, 0, xmitq); - } - } -exit: + if (unlikely(++l->rcv_unacked >= TIPC_MIN_LINK_WIN)) + tipc_link_build_ack_msg(l, xmitq); + + } while ((skb = __skb_dequeue(defq))); + + return rc; +drop: + kfree_skb(skb); return rc; } @@ -1249,7 +1259,7 @@ static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe, } /* tipc_link_tnl_prepare(): prepare and return a list of tunnel packets - * with contents of the link's tranmsit and backlog queues. + * with contents of the link's transmit and backlog queues. */ void tipc_link_tnl_prepare(struct tipc_link *l, struct tipc_link *tnl, int mtyp, struct sk_buff_head *xmitq) -- cgit v0.10.2 From 81204c492b05274ade680c54787cd8ba234dcfd7 Mon Sep 17 00:00:00 2001 From: Jon Paul Maloy Date: Thu, 15 Oct 2015 14:52:42 -0400 Subject: tipc: improve sequence number checking The sequence number of an incoming packet is currently only checked for less than, equality to, or bigger than the next expected number, meaning that the receive window in practice becomes one half sequence number cycle, or U16_MAX/2. This does not make sense, and may not even be safe if there are extreme delays in the network. Any packet sent by the peer during the ongoing cycle must belong inside his current send window, or should otherwise be dropped if possible. Since a link endpoint cannot know its peer's current send window, it has to base this sanity check on a worst-case assumption, i.e., that the peer is using a maximum sized window of 8191 packets. Using this assumption, we now add a check that the sequence number is not bigger than next_expected + TIPC_MAX_LINK_WIN. We also re-order the checks done, so that the receive window test is performed before the gap test. This way, we are guaranteed that no packet with illegal sequence numbers are ever added to the deferred queue. Signed-off-by: Jon Maloy Acked-by: Ying Xue Signed-off-by: David S. Miller diff --git a/net/tipc/link.c b/net/tipc/link.c index 8e23ab5..2b549f6 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -1077,13 +1077,14 @@ int tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb, { struct sk_buff_head *defq = &l->deferdq; struct tipc_msg *hdr; - u16 seqno, rcv_nxt; + u16 seqno, rcv_nxt, win_lim; int rc = 0; do { hdr = buf_msg(skb); seqno = msg_seqno(hdr); rcv_nxt = l->rcv_nxt; + win_lim = rcv_nxt + TIPC_MAX_LINK_WIN; /* Verify and update link state */ if (unlikely(msg_user(hdr) == LINK_PROTOCOL)) @@ -1098,6 +1099,12 @@ int tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb, /* Don't send probe at next timeout expiration */ l->silent_intv_cnt = 0; + /* Drop if outside receive window */ + if (unlikely(less(seqno, rcv_nxt) || more(seqno, win_lim))) { + l->stats.duplicates++; + goto drop; + } + /* Forward queues and wake up waiting users */ if (likely(tipc_link_release_pkts(l, msg_ack(hdr)))) { tipc_link_advance_backlog(l, xmitq); @@ -1105,29 +1112,20 @@ int tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb, link_prepare_wakeup(l); } - /* Defer reception if there is a gap in the sequence */ - if (unlikely(less(rcv_nxt, seqno))) { + /* Defer delivery if sequence gap */ + if (unlikely(seqno != rcv_nxt)) { __tipc_skb_queue_sorted(defq, skb); tipc_link_build_nack_msg(l, xmitq); break; } - /* Drop if packet already received */ - if (unlikely(more(rcv_nxt, seqno))) { - l->stats.duplicates++; - goto drop; - } - - /* Packet can be delivered */ + /* Deliver packet */ l->rcv_nxt++; l->stats.recv_info++; - if (!tipc_data_input(l, skb, l->inputq)) rc = tipc_link_input(l, skb, l->inputq); - if (rc) + if (unlikely(rc)) break; - - /* Ack at regular intervals */ if (unlikely(++l->rcv_unacked >= TIPC_MIN_LINK_WIN)) tipc_link_build_ack_msg(l, xmitq); diff --git a/net/tipc/link.h b/net/tipc/link.h index 39ff8b6..7a1ad42 100644 --- a/net/tipc/link.h +++ b/net/tipc/link.h @@ -185,7 +185,7 @@ struct tipc_link { } backlog[5]; u16 snd_nxt; u16 last_retransm; - u32 window; + u16 window; u32 stale_count; /* Reception */ -- cgit v0.10.2 From 8306f99a517b91ebf8fa94d017c2c84ca62e107c Mon Sep 17 00:00:00 2001 From: Jon Paul Maloy Date: Thu, 15 Oct 2015 14:52:43 -0400 Subject: tipc: disallow packet duplicates in link deferred queue After the previous commits, we are guaranteed that no packets of type LINK_PROTOCOL or with illegal sequence numbers will be attempted added to the link deferred queue. This makes it possible to make some simplifications to the sorting algorithm in the function tipc_skb_queue_sorted(). We also alter the function so that it will drop packets if one with the same seqeunce number is already present in the queue. This is necessary because we have identified weird packet sequences, involving duplicate packets, where a legitimate in-sequence packet may advance to the head of the queue without being detected and de-queued. Finally, we make this function outline, since it will now be called only in exceptional cases. Signed-off-by: Jon Maloy Acked-by: Ying Xue Signed-off-by: David S. Miller diff --git a/net/tipc/link.c b/net/tipc/link.c index 2b549f6..e7c6086 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -1114,7 +1114,7 @@ int tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb, /* Defer delivery if sequence gap */ if (unlikely(seqno != rcv_nxt)) { - __tipc_skb_queue_sorted(defq, skb); + __tipc_skb_queue_sorted(defq, seqno, skb); tipc_link_build_nack_msg(l, xmitq); break; } diff --git a/net/tipc/msg.c b/net/tipc/msg.c index c5ac436..454f5ec 100644 --- a/net/tipc/msg.c +++ b/net/tipc/msg.c @@ -590,3 +590,34 @@ error: kfree_skb(head); return NULL; } + +/* tipc_skb_queue_sorted(); sort pkt into list according to sequence number + * @list: list to be appended to + * @seqno: sequence number of buffer to add + * @skb: buffer to add + */ +void __tipc_skb_queue_sorted(struct sk_buff_head *list, u16 seqno, + struct sk_buff *skb) +{ + struct sk_buff *_skb, *tmp; + + if (skb_queue_empty(list) || less(seqno, buf_seqno(skb_peek(list)))) { + __skb_queue_head(list, skb); + return; + } + + if (more(seqno, buf_seqno(skb_peek_tail(list)))) { + __skb_queue_tail(list, skb); + return; + } + + skb_queue_walk_safe(list, _skb, tmp) { + if (more(seqno, buf_seqno(_skb))) + continue; + if (seqno == buf_seqno(_skb)) + break; + __skb_queue_before(list, _skb, skb); + return; + } + kfree_skb(skb); +} diff --git a/net/tipc/msg.h b/net/tipc/msg.h index a82c584..c784ba0 100644 --- a/net/tipc/msg.h +++ b/net/tipc/msg.h @@ -790,6 +790,8 @@ int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m, int offset, int dsz, int mtu, struct sk_buff_head *list); bool tipc_msg_lookup_dest(struct net *net, struct sk_buff *skb, int *err); struct sk_buff *tipc_msg_reassemble(struct sk_buff_head *list); +void __tipc_skb_queue_sorted(struct sk_buff_head *list, u16 seqno, + struct sk_buff *skb); static inline u16 buf_seqno(struct sk_buff *skb) { @@ -862,38 +864,6 @@ static inline struct sk_buff *tipc_skb_dequeue(struct sk_buff_head *list, return skb; } -/* tipc_skb_queue_sorted(); sort pkt into list according to sequence number - * @list: list to be appended to - * @skb: buffer to add - * Returns true if queue should treated further, otherwise false - */ -static inline bool __tipc_skb_queue_sorted(struct sk_buff_head *list, - struct sk_buff *skb) -{ - struct sk_buff *_skb, *tmp; - struct tipc_msg *hdr = buf_msg(skb); - u16 seqno = msg_seqno(hdr); - - if (skb_queue_empty(list) || (msg_user(hdr) == LINK_PROTOCOL)) { - __skb_queue_head(list, skb); - return true; - } - if (likely(less(seqno, buf_seqno(skb_peek(list))))) { - __skb_queue_head(list, skb); - return true; - } - if (!more(seqno, buf_seqno(skb_peek_tail(list)))) { - skb_queue_walk_safe(list, _skb, tmp) { - if (likely(less(seqno, buf_seqno(_skb)))) { - __skb_queue_before(list, _skb, skb); - return true; - } - } - } - __skb_queue_tail(list, skb); - return false; -} - /* tipc_skb_queue_splice_tail - append an skb list to lock protected list * @list: the new list to append. Not lock protected * @head: target list. Lock protected. -- cgit v0.10.2 From 73f646cec35477b5099d7e952297cb9e1855be45 Mon Sep 17 00:00:00 2001 From: Jon Paul Maloy Date: Thu, 15 Oct 2015 14:52:44 -0400 Subject: tipc: delay ESTABLISH state event when link is established Link establishing, just like link teardown, is a non-atomic action, in the sense that discovering that conditions are right to establish a link, and the actual adding of the link to one of the node's send slots is done in two different lock contexts. The link FSM is designed to help bridging the gap between the two contexts in a safe manner. We have now discovered a weakness in the implementaton of this FSM. Because we directly let the link go from state LINK_ESTABLISHING to state LINK_ESTABLISHED already in the first lock context, we are unable to distinguish between a fully established link, i.e., a link that has been added to its slot, and a link that has not yet reached the second lock context. It may hence happen that a manual intervention, e.g., when disabling an interface, causes the function tipc_node_link_down() to try removing the link from the node slots, decrementing its active link counter etc, although the link was never added there in the first place. We solve this by delaying the actual state change until we reach the second lock context, inside the function tipc_node_link_up(). This makes it possible for potentail callers of __tipc_node_link_down() to know if they should proceed or not, and the problem is solved. Unforunately, the situation described above also has a second problem. Since there by necessity is a tipc_node_link_up() call pending once the node lock has been released, we must defuse that call by setting the link back from LINK_ESTABLISHING to LINK_RESET state. This forces us to make a slight modification to the link FSM, which will now look as follows. +------------------------------------+ |RESET_EVT | | | | +--------------+ | +-----------------| SYNCHING |-----------------+ | |FAILURE_EVT +--------------+ PEER_RESET_EVT| | | A | | | | | | | | | | | | | | |SYNCH_ |SYNCH_ | | | |BEGIN_EVT |END_EVT | | | | | | | V | V V | +-------------+ +--------------+ +------------+ | | RESETTING |<---------| ESTABLISHED |--------->| PEER_RESET | | +-------------+ FAILURE_ +--------------+ PEER_ +------------+ | | EVT | A RESET_EVT | | | | | | | | +----------------+ | | | RESET_EVT| |RESET_EVT | | | | | | | | | | |ESTABLISH_EVT | | | | +-------------+ | | | | | | RESET_EVT | | | | | | | | | | | V V V | | | | +-------------+ +--------------+ RESET_EVT| +--->| RESET |--------->| ESTABLISHING |<----------------+ +-------------+ PEER_ +--------------+ | A RESET_EVT | | | | | | | |FAILOVER_ |FAILOVER_ |FAILOVER_ |BEGIN_EVT |END_EVT |BEGIN_EVT | | | V | | +-------------+ | | FAILINGOVER |<----------------+ +-------------+ Signed-off-by: Jon Maloy Acked-by: Ying Xue Signed-off-by: David S. Miller diff --git a/net/tipc/link.c b/net/tipc/link.c index e7c6086..8c794c1 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -125,6 +125,11 @@ bool tipc_link_is_reset(struct tipc_link *l) return l->state & (LINK_RESET | LINK_FAILINGOVER | LINK_ESTABLISHING); } +bool tipc_link_is_establishing(struct tipc_link *l) +{ + return l->state == LINK_ESTABLISHING; +} + bool tipc_link_is_synching(struct tipc_link *l) { return l->state == LINK_SYNCHING; @@ -321,14 +326,15 @@ int tipc_link_fsm_evt(struct tipc_link *l, int evt) switch (evt) { case LINK_ESTABLISH_EVT: l->state = LINK_ESTABLISHED; - rc |= TIPC_LINK_UP_EVT; break; case LINK_FAILOVER_BEGIN_EVT: l->state = LINK_FAILINGOVER; break; - case LINK_PEER_RESET_EVT: case LINK_RESET_EVT: + l->state = LINK_RESET; + break; case LINK_FAILURE_EVT: + case LINK_PEER_RESET_EVT: case LINK_SYNCH_BEGIN_EVT: case LINK_FAILOVER_END_EVT: break; @@ -1091,9 +1097,9 @@ int tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb, return tipc_link_proto_rcv(l, skb, xmitq); if (unlikely(!link_is_up(l))) { - rc = tipc_link_fsm_evt(l, LINK_ESTABLISH_EVT); - if (!link_is_up(l)) - goto drop; + if (l->state == LINK_ESTABLISHING) + rc = TIPC_LINK_UP_EVT; + goto drop; } /* Don't send probe at next timeout expiration */ @@ -1338,6 +1344,7 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb, u16 peers_tol = msg_link_tolerance(hdr); u16 peers_prio = msg_linkprio(hdr); u16 rcv_nxt = l->rcv_nxt; + int mtyp = msg_type(hdr); char *if_name; int rc = 0; @@ -1347,7 +1354,7 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb, if (link_own_addr(l) > msg_prevnode(hdr)) l->net_plane = msg_net_plane(hdr); - switch (msg_type(hdr)) { + switch (mtyp) { case RESET_MSG: /* Ignore duplicate RESET with old session number */ @@ -1374,12 +1381,14 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb, if (in_range(peers_prio, l->priority + 1, TIPC_MAX_LINK_PRI)) l->priority = peers_prio; - if (msg_type(hdr) == RESET_MSG) { - rc |= tipc_link_fsm_evt(l, LINK_PEER_RESET_EVT); - } else if (!link_is_up(l)) { - tipc_link_fsm_evt(l, LINK_PEER_RESET_EVT); - rc |= tipc_link_fsm_evt(l, LINK_ESTABLISH_EVT); - } + /* ACTIVATE_MSG serves as PEER_RESET if link is already down */ + if ((mtyp == RESET_MSG) || !link_is_up(l)) + rc = tipc_link_fsm_evt(l, LINK_PEER_RESET_EVT); + + /* ACTIVATE_MSG takes up link if it was already locally reset */ + if ((mtyp == ACTIVATE_MSG) && (l->state == LINK_ESTABLISHING)) + rc = TIPC_LINK_UP_EVT; + l->peer_session = msg_session(hdr); l->peer_bearer_id = msg_bearer_id(hdr); if (l->mtu > msg_max_pkt(hdr)) @@ -1396,9 +1405,12 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb, l->stats.recv_states++; if (msg_probe(hdr)) l->stats.recv_probes++; - rc = tipc_link_fsm_evt(l, LINK_ESTABLISH_EVT); - if (!link_is_up(l)) + + if (!link_is_up(l)) { + if (l->state == LINK_ESTABLISHING) + rc = TIPC_LINK_UP_EVT; break; + } /* Send NACK if peer has sent pkts we haven't received yet */ if (more(peers_snd_nxt, rcv_nxt) && !tipc_link_is_synching(l)) diff --git a/net/tipc/link.h b/net/tipc/link.h index 7a1ad42..d42dfc0 100644 --- a/net/tipc/link.h +++ b/net/tipc/link.h @@ -217,6 +217,7 @@ int tipc_link_fsm_evt(struct tipc_link *l, int evt); void tipc_link_reset_fragments(struct tipc_link *l_ptr); bool tipc_link_is_up(struct tipc_link *l); bool tipc_link_is_reset(struct tipc_link *l); +bool tipc_link_is_establishing(struct tipc_link *l); bool tipc_link_is_synching(struct tipc_link *l); bool tipc_link_is_failingover(struct tipc_link *l); bool tipc_link_is_blocked(struct tipc_link *l); diff --git a/net/tipc/node.c b/net/tipc/node.c index 703875f..656b579 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -317,7 +317,11 @@ static void __tipc_node_link_up(struct tipc_node *n, int bearer_id, struct tipc_link *ol = node_active_link(n, 0); struct tipc_link *nl = n->links[bearer_id].link; - if (!nl || !tipc_link_is_up(nl)) + if (!nl) + return; + + tipc_link_fsm_evt(nl, LINK_ESTABLISH_EVT); + if (!tipc_link_is_up(nl)) return; n->working_links++; @@ -437,17 +441,26 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id, static void tipc_node_link_down(struct tipc_node *n, int bearer_id, bool delete) { struct tipc_link_entry *le = &n->links[bearer_id]; + struct tipc_link *l = le->link; struct tipc_media_addr *maddr; struct sk_buff_head xmitq; + if (!l) + return; + __skb_queue_head_init(&xmitq); tipc_node_lock(n); - __tipc_node_link_down(n, &bearer_id, &xmitq, &maddr); - if (delete && le->link) { - kfree(le->link); - le->link = NULL; - n->link_cnt--; + if (!tipc_link_is_establishing(l)) { + __tipc_node_link_down(n, &bearer_id, &xmitq, &maddr); + if (delete) { + kfree(l); + le->link = NULL; + n->link_cnt--; + } + } else { + /* Defuse pending tipc_node_link_up() */ + tipc_link_fsm_evt(l, LINK_RESET_EVT); } tipc_node_unlock(n); @@ -579,7 +592,7 @@ void tipc_node_check_dest(struct net *net, u32 onode, memcpy(&le->maddr, maddr, sizeof(*maddr)); exit: tipc_node_unlock(n); - if (reset) + if (reset && !tipc_link_is_reset(l)) tipc_node_link_down(n, b->identity, false); tipc_node_put(n); } @@ -686,10 +699,10 @@ static void tipc_node_fsm_evt(struct tipc_node *n, int evt) break; case SELF_ESTABL_CONTACT_EVT: case PEER_LOST_CONTACT_EVT: - break; case NODE_SYNCH_END_EVT: - case NODE_SYNCH_BEGIN_EVT: case NODE_FAILOVER_BEGIN_EVT: + break; + case NODE_SYNCH_BEGIN_EVT: case NODE_FAILOVER_END_EVT: default: goto illegal_evt; -- cgit v0.10.2 From 282b3a056225b35024246f63feb91d769d714dad Mon Sep 17 00:00:00 2001 From: Jon Paul Maloy Date: Thu, 15 Oct 2015 14:52:45 -0400 Subject: tipc: send out RESET immediately when link goes down When a link is taken down because of a node local event, such as disabling of a bearer or an interface, we currently leave it to the peer node to discover the broken communication. The default time for such failure discovery is 1.5-2 seconds. If we instead allow the terminating link endpoint to send out a RESET message at the moment it is reset, we can achieve the impression that both endpoints are going down instantly. Since this is a very common scenario, we find it worthwhile to make this small modification. Apart from letting the link produce the said message, we also have to ensure that the interface is able to transmit it before TIPC is detached. We do this by performing the disabling of a bearer in three steps: 1) Disable reception of TIPC packets from the interface in question. 2) Take down the links, while allowing them so send out a RESET message. 3) Disable transmission of TIPC packets on the interface. Apart from this, we now have to react on the NETDEV_GOING_DOWN event, instead of as currently the NEDEV_DOWN event, to ensure that such transmission is possible during the teardown phase. Signed-off-by: Jon Maloy Acked-by: Ying Xue Signed-off-by: David S. Miller diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index ce9f7bf..82b2786 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -362,6 +362,7 @@ static void bearer_disable(struct net *net, struct tipc_bearer *b_ptr) b_ptr->media->disable_media(b_ptr); tipc_node_delete_links(net, b_ptr->identity); + RCU_INIT_POINTER(b_ptr->media_ptr, NULL); if (b_ptr->link_req) tipc_disc_delete(b_ptr->link_req); @@ -399,16 +400,13 @@ int tipc_enable_l2_media(struct net *net, struct tipc_bearer *b, /* tipc_disable_l2_media - detach TIPC bearer from an L2 interface * - * Mark L2 bearer as inactive so that incoming buffers are thrown away, - * then get worker thread to complete bearer cleanup. (Can't do cleanup - * here because cleanup code needs to sleep and caller holds spinlocks.) + * Mark L2 bearer as inactive so that incoming buffers are thrown away */ void tipc_disable_l2_media(struct tipc_bearer *b) { struct net_device *dev; dev = (struct net_device *)rtnl_dereference(b->media_ptr); - RCU_INIT_POINTER(b->media_ptr, NULL); RCU_INIT_POINTER(dev->tipc_ptr, NULL); synchronize_net(); dev_put(dev); @@ -554,7 +552,7 @@ static int tipc_l2_device_event(struct notifier_block *nb, unsigned long evt, case NETDEV_CHANGE: if (netif_carrier_ok(dev)) break; - case NETDEV_DOWN: + case NETDEV_GOING_DOWN: case NETDEV_CHANGEMTU: tipc_reset_bearer(net, b_ptr); break; diff --git a/net/tipc/link.c b/net/tipc/link.c index 8c794c1..737b598 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -1062,6 +1062,18 @@ void tipc_link_build_ack_msg(struct tipc_link *l, struct sk_buff_head *xmitq) tipc_link_build_proto_msg(l, STATE_MSG, 0, 0, 0, 0, xmitq); } +/* tipc_link_build_reset_msg: prepare link RESET or ACTIVATE message + */ +void tipc_link_build_reset_msg(struct tipc_link *l, struct sk_buff_head *xmitq) +{ + int mtyp = RESET_MSG; + + if (l->state == LINK_ESTABLISHING) + mtyp = ACTIVATE_MSG; + + tipc_link_build_proto_msg(l, mtyp, 0, 0, 0, 0, xmitq); +} + /* tipc_link_build_nack_msg: prepare link nack message for transmission */ static void tipc_link_build_nack_msg(struct tipc_link *l, diff --git a/net/tipc/link.h b/net/tipc/link.h index d42dfc0..5872f09 100644 --- a/net/tipc/link.h +++ b/net/tipc/link.h @@ -213,6 +213,7 @@ void tipc_link_tnl_prepare(struct tipc_link *l, struct tipc_link *tnl, int mtyp, struct sk_buff_head *xmitq); void tipc_link_build_bcast_sync_msg(struct tipc_link *l, struct sk_buff_head *xmitq); +void tipc_link_build_reset_msg(struct tipc_link *l, struct sk_buff_head *xmitq); int tipc_link_fsm_evt(struct tipc_link *l, int evt); void tipc_link_reset_fragments(struct tipc_link *l_ptr); bool tipc_link_is_up(struct tipc_link *l); diff --git a/net/tipc/node.c b/net/tipc/node.c index 656b579..fba6e1a 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -41,7 +41,7 @@ #include "socket.h" #include "bcast.h" #include "discover.h" - +#define pr_debug printk /* Node FSM states and events: */ enum { @@ -421,6 +421,8 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id, if (!tipc_node_is_up(n)) { tipc_link_reset(l); + tipc_link_build_reset_msg(l, xmitq); + *maddr = &n->links[*bearer_id].maddr; node_lost_contact(n, &le->inputq); return; } @@ -463,7 +465,6 @@ static void tipc_node_link_down(struct tipc_node *n, int bearer_id, bool delete) tipc_link_fsm_evt(l, LINK_RESET_EVT); } tipc_node_unlock(n); - tipc_bearer_xmit(n->net, bearer_id, &xmitq, maddr); tipc_sk_rcv(n->net, &le->inputq); } diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c index c170d31..9bc0b1e 100644 --- a/net/tipc/udp_media.c +++ b/net/tipc/udp_media.c @@ -425,7 +425,6 @@ static void tipc_udp_disable(struct tipc_bearer *b) } if (ub->ubsock) sock_set_flag(ub->ubsock->sk, SOCK_DEAD); - RCU_INIT_POINTER(b->media_ptr, NULL); RCU_INIT_POINTER(ub->bearer, NULL); /* sock_release need to be done outside of rtnl lock */ -- cgit v0.10.2 From c819930090fe3f74c822be765c185b3431360193 Mon Sep 17 00:00:00 2001 From: Jon Paul Maloy Date: Thu, 15 Oct 2015 14:52:46 -0400 Subject: tipc: update node FSM when peer RESET message is received The change made in the previous commit revealed a small flaw in the way the node FSM is updated. When the function tipc_node_link_down() is called for the last link to a node, we should check whether this was caused by a local reset or by a received RESET message from the peer. In the latter case, we can directly issue a PEER_LOST_CONTACT_EVT to the node FSM, so that it is ready to re-establish contact. If this is not done, the peer node will sometimes have to go through a second establish cycle before the link becomes stable. We fix this in this commit by conditionally issuing the mentioned event in the function tipc_node_link_down(). We also move LINK_RESET FSM even away from the link_reset() function and into the caller function, partially because it is easier to follow the code when state changes are gathered at a limited number of locations, partially because there will be cases in future commits where we don't want the link to go RESET mode when link_reset() is called. Signed-off-by: Jon Maloy Acked-by: Ying Xue Signed-off-by: David S. Miller diff --git a/net/tipc/link.c b/net/tipc/link.c index 737b598..ff9b0b9 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -120,6 +120,11 @@ bool tipc_link_is_up(struct tipc_link *l) return link_is_up(l); } +bool tipc_link_peer_is_down(struct tipc_link *l) +{ + return l->state == LINK_PEER_RESET; +} + bool tipc_link_is_reset(struct tipc_link *l) { return l->state & (LINK_RESET | LINK_FAILINGOVER | LINK_ESTABLISHING); @@ -584,8 +589,6 @@ void tipc_link_purge_queues(struct tipc_link *l_ptr) void tipc_link_reset(struct tipc_link *l) { - tipc_link_fsm_evt(l, LINK_RESET_EVT); - /* Link is down, accept any session */ l->peer_session = WILDCARD_SESSION; diff --git a/net/tipc/link.h b/net/tipc/link.h index 5872f09..0201212 100644 --- a/net/tipc/link.h +++ b/net/tipc/link.h @@ -217,6 +217,7 @@ void tipc_link_build_reset_msg(struct tipc_link *l, struct sk_buff_head *xmitq); int tipc_link_fsm_evt(struct tipc_link *l, int evt); void tipc_link_reset_fragments(struct tipc_link *l_ptr); bool tipc_link_is_up(struct tipc_link *l); +bool tipc_link_peer_is_down(struct tipc_link *l); bool tipc_link_is_reset(struct tipc_link *l); bool tipc_link_is_establishing(struct tipc_link *l); bool tipc_link_is_synching(struct tipc_link *l); diff --git a/net/tipc/node.c b/net/tipc/node.c index fba6e1a..d1f3401 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -41,7 +41,7 @@ #include "socket.h" #include "bcast.h" #include "discover.h" -#define pr_debug printk + /* Node FSM states and events: */ enum { @@ -420,6 +420,10 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id, } if (!tipc_node_is_up(n)) { + if (tipc_link_peer_is_down(l)) + tipc_node_fsm_evt(n, PEER_LOST_CONTACT_EVT); + tipc_node_fsm_evt(n, SELF_LOST_CONTACT_EVT); + tipc_link_fsm_evt(l, LINK_RESET_EVT); tipc_link_reset(l); tipc_link_build_reset_msg(l, xmitq); *maddr = &n->links[*bearer_id].maddr; @@ -434,6 +438,7 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id, n->sync_point = tnl->rcv_nxt + (U16_MAX / 2 - 1); tipc_link_tnl_prepare(l, tnl, FAILOVER_MSG, xmitq); tipc_link_reset(l); + tipc_link_fsm_evt(l, LINK_RESET_EVT); tipc_link_fsm_evt(l, LINK_FAILOVER_BEGIN_EVT); tipc_node_fsm_evt(n, NODE_FAILOVER_BEGIN_EVT); *maddr = &n->links[tnl->bearer_id].maddr; @@ -581,6 +586,7 @@ void tipc_node_check_dest(struct net *net, u32 onode, goto exit; } tipc_link_reset(l); + tipc_link_fsm_evt(l, LINK_RESET_EVT); if (n->state == NODE_FAILINGOVER) tipc_link_fsm_evt(l, LINK_FAILOVER_BEGIN_EVT); le->link = l; @@ -863,9 +869,6 @@ static void node_lost_contact(struct tipc_node *n_ptr, tipc_link_fsm_evt(l, LINK_FAILOVER_END_EVT); } - /* Prevent re-contact with node until cleanup is done */ - tipc_node_fsm_evt(n_ptr, SELF_LOST_CONTACT_EVT); - /* Notify publications from this node */ n_ptr->action_flags |= TIPC_NOTIFY_NODE_DOWN; -- cgit v0.10.2 From 47ea0325337b166c1c8695119aa6e83cdc035ef5 Mon Sep 17 00:00:00 2001 From: Ivan Vecera Date: Thu, 15 Oct 2015 21:28:52 +0200 Subject: drivers/net: get rid of unnecessary initializations in .get_drvinfo() Many drivers initialize uselessly n_priv_flags, n_stats, testinfo_len, eedump_len & regdump_len fields in their .get_drvinfo() ethtool op. It's not necessary as these fields is filled in ethtool_get_drvinfo(). v2: removed unused variable v3: removed another unused variable Signed-off-by: Ivan Vecera Signed-off-by: David S. Miller diff --git a/drivers/infiniband/hw/nes/nes_nic.c b/drivers/infiniband/hw/nes/nes_nic.c index 70acda9..6a0bdfa 100644 --- a/drivers/infiniband/hw/nes/nes_nic.c +++ b/drivers/infiniband/hw/nes/nes_nic.c @@ -1325,9 +1325,6 @@ static void nes_netdev_get_drvinfo(struct net_device *netdev, "%u.%u", nesadapter->firmware_version >> 16, nesadapter->firmware_version & 0x000000ff); strlcpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version)); - drvinfo->testinfo_len = 0; - drvinfo->eedump_len = 0; - drvinfo->regdump_len = 0; } diff --git a/drivers/net/ethernet/aeroflex/greth.c b/drivers/net/ethernet/aeroflex/greth.c index ae89de7..20bf55d 100644 --- a/drivers/net/ethernet/aeroflex/greth.c +++ b/drivers/net/ethernet/aeroflex/greth.c @@ -1141,8 +1141,6 @@ static void greth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *in strlcpy(info->version, "revision: 1.0", sizeof(info->version)); strlcpy(info->bus_info, greth->dev->bus->name, sizeof(info->bus_info)); strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); - info->eedump_len = 0; - info->regdump_len = sizeof(struct greth_regs); } static void greth_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) diff --git a/drivers/net/ethernet/amd/au1000_eth.c b/drivers/net/ethernet/amd/au1000_eth.c index cb367cc..5330bcb 100644 --- a/drivers/net/ethernet/amd/au1000_eth.c +++ b/drivers/net/ethernet/amd/au1000_eth.c @@ -714,7 +714,6 @@ au1000_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) strlcpy(info->version, DRV_VERSION, sizeof(info->version)); snprintf(info->bus_info, sizeof(info->bus_info), "%s %d", DRV_NAME, aup->mac_id); - info->regdump_len = 0; } static void au1000_set_msglevel(struct net_device *dev, u32 value) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c index 204fb3a..6040293 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c @@ -375,7 +375,6 @@ static void xgbe_get_drvinfo(struct net_device *netdev, XGMAC_GET_BITS(hw_feat->version, MAC_VR, USERVER), XGMAC_GET_BITS(hw_feat->version, MAC_VR, DEVID), XGMAC_GET_BITS(hw_feat->version, MAC_VR, SNPSVER)); - drvinfo->n_stats = XGBE_STATS_COUNT; } static u32 xgbe_get_msglevel(struct net_device *netdev) diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c b/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c index 48694c2..872b7ab 100644 --- a/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c @@ -233,10 +233,6 @@ static void atl1c_get_drvinfo(struct net_device *netdev, sizeof(drvinfo->version)); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->n_stats = 0; - drvinfo->testinfo_len = 0; - drvinfo->regdump_len = atl1c_get_regs_len(netdev); - drvinfo->eedump_len = atl1c_get_eeprom_len(netdev); } static void atl1c_get_wol(struct net_device *netdev, diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c b/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c index 1be072f..8e3dbd4 100644 --- a/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c +++ b/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c @@ -316,10 +316,6 @@ static void atl1e_get_drvinfo(struct net_device *netdev, strlcpy(drvinfo->fw_version, "L1e", sizeof(drvinfo->fw_version)); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->n_stats = 0; - drvinfo->testinfo_len = 0; - drvinfo->regdump_len = atl1e_get_regs_len(netdev); - drvinfo->eedump_len = atl1e_get_eeprom_len(netdev); } static void atl1e_get_wol(struct net_device *netdev, diff --git a/drivers/net/ethernet/atheros/atlx/atl1.c b/drivers/net/ethernet/atheros/atlx/atl1.c index eca1d11..529bca7 100644 --- a/drivers/net/ethernet/atheros/atlx/atl1.c +++ b/drivers/net/ethernet/atheros/atlx/atl1.c @@ -3388,7 +3388,6 @@ static void atl1_get_drvinfo(struct net_device *netdev, sizeof(drvinfo->version)); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->eedump_len = ATL1_EEDUMP_LEN; } static void atl1_get_wol(struct net_device *netdev, diff --git a/drivers/net/ethernet/atheros/atlx/atl2.c b/drivers/net/ethernet/atheros/atlx/atl2.c index 46a5353..8f76f45 100644 --- a/drivers/net/ethernet/atheros/atlx/atl2.c +++ b/drivers/net/ethernet/atheros/atlx/atl2.c @@ -2030,10 +2030,6 @@ static void atl2_get_drvinfo(struct net_device *netdev, strlcpy(drvinfo->fw_version, "L2", sizeof(drvinfo->fw_version)); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->n_stats = 0; - drvinfo->testinfo_len = 0; - drvinfo->regdump_len = atl2_get_regs_len(netdev); - drvinfo->eedump_len = atl2_get_eeprom_len(netdev); } static void atl2_get_wol(struct net_device *netdev, diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index a7f2cc3..95af75d 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -1333,7 +1333,6 @@ static void bcm_enet_get_drvinfo(struct net_device *netdev, sizeof(drvinfo->version)); strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); strlcpy(drvinfo->bus_info, "bcm63xx", sizeof(drvinfo->bus_info)); - drvinfo->n_stats = BCM_ENET_STATS_LEN; } static int bcm_enet_get_sset_count(struct net_device *netdev, @@ -2597,7 +2596,6 @@ static void bcm_enetsw_get_drvinfo(struct net_device *netdev, strncpy(drvinfo->version, bcm_enet_driver_version, 32); strncpy(drvinfo->fw_version, "N/A", 32); strncpy(drvinfo->bus_info, "bcm63xx", 32); - drvinfo->n_stats = BCM_ENETSW_STATS_LEN; } static void bcm_enetsw_get_ethtool_stats(struct net_device *netdev, diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index f1b5364..8581063 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -287,7 +287,6 @@ static void bcm_sysport_get_drvinfo(struct net_device *dev, strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); strlcpy(info->version, "0.1", sizeof(info->version)); strlcpy(info->bus_info, "platform", sizeof(info->bus_info)); - info->n_stats = BCM_SYSPORT_STATS_LEN; } static u32 bcm_sysport_get_msglvl(struct net_device *dev) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index aeb7ce6..a2bc531 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -1090,10 +1090,6 @@ static void bnx2x_get_drvinfo(struct net_device *dev, bnx2x_fill_fw_str(bp, info->fw_version, sizeof(info->fw_version)); strlcpy(info->bus_info, pci_name(bp->pdev), sizeof(info->bus_info)); - info->n_stats = BNX2X_NUM_STATS; - info->testinfo_len = BNX2X_NUM_TESTS(bp); - info->eedump_len = bp->common.flash_size; - info->regdump_len = bnx2x_get_regs_len(dev); } static void bnx2x_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 1a3988f..410995c 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -793,7 +793,6 @@ static void bcmgenet_get_drvinfo(struct net_device *dev, { strlcpy(info->driver, "bcmgenet", sizeof(info->driver)); strlcpy(info->version, "v2.0", sizeof(info->version)); - info->n_stats = BCMGENET_STATS_LEN; } static int bcmgenet_get_sset_count(struct net_device *dev, int string_set) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c index 29f3308..245c063 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c @@ -153,7 +153,6 @@ lio_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) strncpy(drvinfo->fw_version, oct->fw_info.liquidio_firmware_version, ETHTOOL_FWVERS_LEN); strncpy(drvinfo->bus_info, pci_name(oct->pci_dev), 32); - drvinfo->regdump_len = OCT_ETHTOOL_REGDUMP_LEN; } static void diff --git a/drivers/net/ethernet/dec/tulip/de2104x.c b/drivers/net/ethernet/dec/tulip/de2104x.c index a02ecc4..cadcee6 100644 --- a/drivers/net/ethernet/dec/tulip/de2104x.c +++ b/drivers/net/ethernet/dec/tulip/de2104x.c @@ -1597,7 +1597,6 @@ static void de_get_drvinfo (struct net_device *dev,struct ethtool_drvinfo *info) strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); strlcpy(info->version, DRV_VERSION, sizeof(info->version)); strlcpy(info->bus_info, pci_name(de->pdev), sizeof(info->bus_info)); - info->eedump_len = DE_EEPROM_SIZE; } static int de_get_regs_len(struct net_device *dev) diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c index 2c9ed17..f4cb8e4 100644 --- a/drivers/net/ethernet/emulex/benet/be_ethtool.c +++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c @@ -234,9 +234,6 @@ static void be_get_drvinfo(struct net_device *netdev, strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->testinfo_len = 0; - drvinfo->regdump_len = 0; - drvinfo->eedump_len = 0; } static u32 lancer_cmd_get_file_len(struct be_adapter *adapter, u8 *file_name) diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c index fb7f8d6..928ca2b 100644 --- a/drivers/net/ethernet/freescale/gianfar_ethtool.c +++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c @@ -182,8 +182,6 @@ static void gfar_gdrvinfo(struct net_device *dev, sizeof(drvinfo->version)); strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); strlcpy(drvinfo->bus_info, "N/A", sizeof(drvinfo->bus_info)); - drvinfo->regdump_len = 0; - drvinfo->eedump_len = 0; } diff --git a/drivers/net/ethernet/freescale/ucc_geth_ethtool.c b/drivers/net/ethernet/freescale/ucc_geth_ethtool.c index cc83350..89714f5 100644 --- a/drivers/net/ethernet/freescale/ucc_geth_ethtool.c +++ b/drivers/net/ethernet/freescale/ucc_geth_ethtool.c @@ -351,8 +351,6 @@ uec_get_drvinfo(struct net_device *netdev, strlcpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version)); strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); strlcpy(drvinfo->bus_info, "QUICC ENGINE", sizeof(drvinfo->bus_info)); - drvinfo->eedump_len = 0; - drvinfo->regdump_len = uec_get_regs_len(netdev); } #ifdef CONFIG_PM diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c index 2550208..7d58918 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c @@ -682,7 +682,6 @@ static void hns_nic_get_drvinfo(struct net_device *net_dev, drvinfo->bus_info[ETHTOOL_BUSINFO_LEN - 1] = '\0'; strncpy(drvinfo->fw_version, "N/A", ETHTOOL_FWVERS_LEN); - drvinfo->eedump_len = 0; } /** diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index b60a34d..5d7db6c 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -2204,7 +2204,6 @@ static void emac_ethtool_get_drvinfo(struct net_device *ndev, strlcpy(info->version, DRV_VERSION, sizeof(info->version)); snprintf(info->bus_info, sizeof(info->bus_info), "PPC 4xx EMAC-%d %s", dev->cell_index, dev->ofdev->dev.of_node->full_name); - info->regdump_len = emac_ethtool_get_regs_len(ndev); } static const struct ethtool_ops emac_ethtool_ops = { diff --git a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c index 4270ad2..83e557c 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c +++ b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c @@ -559,8 +559,6 @@ static void e1000_get_drvinfo(struct net_device *netdev, strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->regdump_len = e1000_get_regs_len(netdev); - drvinfo->eedump_len = e1000_get_eeprom_len(netdev); } static void e1000_get_ringparam(struct net_device *netdev, diff --git a/drivers/net/ethernet/intel/e1000e/ethtool.c b/drivers/net/ethernet/intel/e1000e/ethtool.c index ad6daa6..6cab1f3 100644 --- a/drivers/net/ethernet/intel/e1000e/ethtool.c +++ b/drivers/net/ethernet/intel/e1000e/ethtool.c @@ -648,8 +648,6 @@ static void e1000_get_drvinfo(struct net_device *netdev, strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->regdump_len = e1000_get_regs_len(netdev); - drvinfo->eedump_len = e1000_get_eeprom_len(netdev); } static void e1000_get_ringparam(struct net_device *netdev, diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c index 447a5f8..2ce0eba 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c @@ -515,10 +515,6 @@ static void fm10k_get_drvinfo(struct net_device *dev, sizeof(info->version) - 1); strncpy(info->bus_info, pci_name(interface->pdev), sizeof(info->bus_info) - 1); - - info->n_stats = fm10k_get_sset_count(dev, ETH_SS_STATS); - - info->regdump_len = fm10k_get_regs_len(dev); } static void fm10k_get_pauseparam(struct net_device *dev, diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index ed70a3f..e7fd84c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -1126,7 +1126,6 @@ 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; } static void i40e_get_ringparam(struct net_device *netdev, diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index 7426276..2529bc6 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -842,10 +842,6 @@ static void igb_get_drvinfo(struct net_device *netdev, sizeof(drvinfo->fw_version)); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->n_stats = IGB_STATS_LEN; - drvinfo->testinfo_len = IGB_TEST_LEN; - drvinfo->regdump_len = igb_get_regs_len(netdev); - drvinfo->eedump_len = igb_get_eeprom_len(netdev); } static void igb_get_ringparam(struct net_device *netdev, diff --git a/drivers/net/ethernet/intel/igbvf/ethtool.c b/drivers/net/ethernet/intel/igbvf/ethtool.c index c6996fe..b74ce53 100644 --- a/drivers/net/ethernet/intel/igbvf/ethtool.c +++ b/drivers/net/ethernet/intel/igbvf/ethtool.c @@ -196,8 +196,6 @@ static void igbvf_get_drvinfo(struct net_device *netdev, sizeof(drvinfo->version)); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->regdump_len = igbvf_get_regs_len(netdev); - drvinfo->eedump_len = igbvf_get_eeprom_len(netdev); } static void igbvf_get_ringparam(struct net_device *netdev, diff --git a/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c b/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c index b311e9e..d2b29b4 100644 --- a/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c +++ b/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c @@ -479,9 +479,6 @@ ixgb_get_drvinfo(struct net_device *netdev, sizeof(drvinfo->version)); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->n_stats = IXGB_STATS_LEN; - drvinfo->regdump_len = ixgb_get_regs_len(netdev); - drvinfo->eedump_len = ixgb_get_eeprom_len(netdev); } static void diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index 94c4912..d681273 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -943,9 +943,6 @@ static void ixgbe_get_drvinfo(struct net_device *netdev, strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->n_stats = IXGBE_STATS_LEN; - drvinfo->testinfo_len = IXGBE_TEST_LEN; - drvinfo->regdump_len = ixgbe_get_regs_len(netdev); } static void ixgbe_get_ringparam(struct net_device *netdev, diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index c78ae18..603d29d 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -1586,7 +1586,6 @@ static void mv643xx_eth_get_drvinfo(struct net_device *dev, sizeof(drvinfo->version)); strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); strlcpy(drvinfo->bus_info, "platform", sizeof(drvinfo->bus_info)); - drvinfo->n_stats = ARRAY_SIZE(mv643xx_eth_stats); } static int mv643xx_eth_nway_reset(struct net_device *dev) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c index f79d812..ddb5541 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c @@ -95,9 +95,6 @@ mlx4_en_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) (u16) (mdev->dev->caps.fw_ver & 0xffff)); strlcpy(drvinfo->bus_info, pci_name(mdev->dev->persist->pdev), sizeof(drvinfo->bus_info)); - drvinfo->n_stats = 0; - drvinfo->regdump_len = 0; - drvinfo->eedump_len = 0; } static const char mlx4_en_priv_flags[][ETH_GSTRING_LEN] = { diff --git a/drivers/net/ethernet/neterion/s2io.c b/drivers/net/ethernet/neterion/s2io.c index 2d1b942..9ba9758 100644 --- a/drivers/net/ethernet/neterion/s2io.c +++ b/drivers/net/ethernet/neterion/s2io.c @@ -5389,8 +5389,6 @@ static void s2io_ethtool_gdrvinfo(struct net_device *dev, strlcpy(info->driver, s2io_driver_name, sizeof(info->driver)); strlcpy(info->version, s2io_driver_version, sizeof(info->version)); strlcpy(info->bus_info, pci_name(sp->pdev), sizeof(info->bus_info)); - info->regdump_len = XENA_REG_SPACE; - info->eedump_len = XENA_EEPROM_SPACE; } /** diff --git a/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c b/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c index be916eb..9a29670 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c +++ b/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c @@ -105,10 +105,6 @@ static void vxge_ethtool_gdrvinfo(struct net_device *dev, strlcpy(info->version, DRV_VERSION, sizeof(info->version)); strlcpy(info->fw_version, vdev->fw_version, sizeof(info->fw_version)); strlcpy(info->bus_info, pci_name(vdev->pdev), sizeof(info->bus_info)); - info->regdump_len = sizeof(struct vxge_hw_vpath_reg) - * vdev->no_of_vpath; - - info->n_stats = STAT_LEN; } /** diff --git a/drivers/net/ethernet/octeon/octeon_mgmt.c b/drivers/net/ethernet/octeon/octeon_mgmt.c index 7bf9c02..c177c7c 100644 --- a/drivers/net/ethernet/octeon/octeon_mgmt.c +++ b/drivers/net/ethernet/octeon/octeon_mgmt.c @@ -1344,10 +1344,6 @@ static void octeon_mgmt_get_drvinfo(struct net_device *netdev, strlcpy(info->version, DRV_VERSION, sizeof(info->version)); strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); strlcpy(info->bus_info, "N/A", sizeof(info->bus_info)); - info->n_stats = 0; - info->testinfo_len = 0; - info->regdump_len = 0; - info->eedump_len = 0; } static int octeon_mgmt_get_settings(struct net_device *netdev, diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c index f6fcf74..b19be7c 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c @@ -164,7 +164,6 @@ static void pch_gbe_get_drvinfo(struct net_device *netdev, strlcpy(drvinfo->version, pch_driver_version, sizeof(drvinfo->version)); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->regdump_len = pch_gbe_get_regs_len(netdev); } /** diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c index 87e073c6..f903446 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c @@ -93,8 +93,6 @@ netxen_nic_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->regdump_len = NETXEN_NIC_REGS_LEN; - drvinfo->eedump_len = netxen_nic_get_eeprom_len(dev); } static int diff --git a/drivers/net/ethernet/qlogic/qla3xxx.c b/drivers/net/ethernet/qlogic/qla3xxx.c index 4847713..b09a6b8 100644 --- a/drivers/net/ethernet/qlogic/qla3xxx.c +++ b/drivers/net/ethernet/qlogic/qla3xxx.c @@ -1736,8 +1736,6 @@ static void ql_get_drvinfo(struct net_device *ndev, sizeof(drvinfo->version)); strlcpy(drvinfo->bus_info, pci_name(qdev->pdev), sizeof(drvinfo->bus_info)); - drvinfo->regdump_len = 0; - drvinfo->eedump_len = 0; } static u32 ql_get_msglevel(struct net_device *ndev) diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_ethtool.c b/drivers/net/ethernet/qlogic/qlge/qlge_ethtool.c index c3c514e..5dade1f 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge_ethtool.c +++ b/drivers/net/ethernet/qlogic/qlge/qlge_ethtool.c @@ -415,13 +415,6 @@ static void ql_get_drvinfo(struct net_device *ndev, (qdev->fw_rev_id & 0x000000ff)); strlcpy(drvinfo->bus_info, pci_name(qdev->pdev), sizeof(drvinfo->bus_info)); - drvinfo->n_stats = 0; - drvinfo->testinfo_len = 0; - if (!test_bit(QL_FRC_COREDUMP, &qdev->flags)) - drvinfo->regdump_len = sizeof(struct ql_mpi_coredump); - else - drvinfo->regdump_len = sizeof(struct ql_reg_dump); - drvinfo->eedump_len = 0; } static void ql_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) diff --git a/drivers/net/ethernet/realtek/8139too.c b/drivers/net/ethernet/realtek/8139too.c index 78bb4ce..ef668d3 100644 --- a/drivers/net/ethernet/realtek/8139too.c +++ b/drivers/net/ethernet/realtek/8139too.c @@ -2388,7 +2388,6 @@ static void rtl8139_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo * strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); strlcpy(info->version, DRV_VERSION, sizeof(info->version)); strlcpy(info->bus_info, pci_name(tp->pci_dev), sizeof(info->bus_info)); - info->regdump_len = tp->regs_len; } static int rtl8139_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) diff --git a/drivers/net/ethernet/sun/cassini.c b/drivers/net/ethernet/sun/cassini.c index 6ce9731..062bce9 100644 --- a/drivers/net/ethernet/sun/cassini.c +++ b/drivers/net/ethernet/sun/cassini.c @@ -4529,9 +4529,6 @@ static void cas_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); strlcpy(info->bus_info, pci_name(cp->pdev), sizeof(info->bus_info)); - info->regdump_len = cp->casreg_len < CAS_MAX_REGS ? - cp->casreg_len : CAS_MAX_REGS; - info->n_stats = CAS_NUM_STAT_KEYS; } static int cas_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) diff --git a/drivers/net/ethernet/tehuti/tehuti.c b/drivers/net/ethernet/tehuti/tehuti.c index a9cac84..14c9d1b 100644 --- a/drivers/net/ethernet/tehuti/tehuti.c +++ b/drivers/net/ethernet/tehuti/tehuti.c @@ -2182,11 +2182,6 @@ bdx_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); strlcpy(drvinfo->bus_info, pci_name(priv->pdev), sizeof(drvinfo->bus_info)); - - drvinfo->n_stats = ((priv->stats_flag) ? ARRAY_SIZE(bdx_stat_names) : 0); - drvinfo->testinfo_len = 0; - drvinfo->regdump_len = 0; - drvinfo->eedump_len = 0; } /* diff --git a/drivers/net/ethernet/ti/cpmac.c b/drivers/net/ethernet/ti/cpmac.c index cba3d9f..77d26fe 100644 --- a/drivers/net/ethernet/ti/cpmac.c +++ b/drivers/net/ethernet/ti/cpmac.c @@ -899,7 +899,6 @@ static void cpmac_get_drvinfo(struct net_device *dev, strlcpy(info->driver, "cpmac", sizeof(info->driver)); strlcpy(info->version, CPMAC_VERSION, sizeof(info->version)); snprintf(info->bus_info, sizeof(info->bus_info), "%s", "cpmac"); - info->regdump_len = 0; } static const struct ethtool_ops cpmac_ethtool_ops = { diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 75584cc..3b75adf 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1784,7 +1784,6 @@ static void cpsw_get_drvinfo(struct net_device *ndev, strlcpy(info->driver, "cpsw", sizeof(info->driver)); strlcpy(info->version, "1.0", sizeof(info->version)); strlcpy(info->bus_info, priv->pdev->name, sizeof(info->bus_info)); - info->regdump_len = cpsw_get_regs_len(ndev); } static u32 cpsw_get_msglevel(struct net_device *ndev) diff --git a/drivers/net/ethernet/ti/tlan.c b/drivers/net/ethernet/ti/tlan.c index 691ec93..a274cd4 100644 --- a/drivers/net/ethernet/ti/tlan.c +++ b/drivers/net/ethernet/ti/tlan.c @@ -791,7 +791,6 @@ static void tlan_get_drvinfo(struct net_device *dev, sizeof(info->bus_info)); else strlcpy(info->bus_info, "EISA", sizeof(info->bus_info)); - info->eedump_len = TLAN_EEPROM_SIZE; } static int tlan_get_eeprom_len(struct net_device *dev) diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index d95f9aa..4684644 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -1135,7 +1135,6 @@ static void axienet_ethtools_get_drvinfo(struct net_device *ndev, { strlcpy(ed->driver, DRIVER_NAME, sizeof(ed->driver)); strlcpy(ed->version, DRIVER_VERSION, sizeof(ed->version)); - ed->regdump_len = sizeof(u32) * AXIENET_REGS_N; } /** diff --git a/drivers/net/fjes/fjes_ethtool.c b/drivers/net/fjes/fjes_ethtool.c index 0119dd1..9c218e1 100644 --- a/drivers/net/fjes/fjes_ethtool.c +++ b/drivers/net/fjes/fjes_ethtool.c @@ -105,8 +105,6 @@ static void fjes_get_drvinfo(struct net_device *netdev, strlcpy(drvinfo->fw_version, "none", sizeof(drvinfo->fw_version)); snprintf(drvinfo->bus_info, sizeof(drvinfo->bus_info), "platform:%s", plat_dev->name); - drvinfo->regdump_len = 0; - drvinfo->eedump_len = 0; } static int fjes_get_settings(struct net_device *netdev, diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c index a186b0a..bd9acff 100644 --- a/drivers/net/usb/asix_common.c +++ b/drivers/net/usb/asix_common.c @@ -588,7 +588,6 @@ void asix_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) usbnet_get_drvinfo(net, info); strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver)); strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); - info->eedump_len = AX_EEPROM_LEN; } int asix_set_mac_address(struct net_device *net, void *p) diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c index 6e9c344..0b4bdd3 100644 --- a/drivers/net/usb/dm9601.c +++ b/drivers/net/usb/dm9601.c @@ -258,7 +258,6 @@ static void dm9601_get_drvinfo(struct net_device *net, { /* Inherit standard device info */ usbnet_get_drvinfo(net, info); - info->eedump_len = DM_EEPROM_LEN; } static u32 dm9601_get_link(struct net_device *net) diff --git a/drivers/net/usb/mcs7830.c b/drivers/net/usb/mcs7830.c index 82d844a..4f345bd 100644 --- a/drivers/net/usb/mcs7830.c +++ b/drivers/net/usb/mcs7830.c @@ -445,7 +445,6 @@ static int mcs7830_get_regs_len(struct net_device *net) static void mcs7830_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *drvinfo) { usbnet_get_drvinfo(net, drvinfo); - drvinfo->regdump_len = mcs7830_get_regs_len(net); } static void mcs7830_get_regs(struct net_device *net, struct ethtool_regs *regs, void *data) diff --git a/drivers/net/usb/sr9800.c b/drivers/net/usb/sr9800.c index 953de13..a50df0d 100644 --- a/drivers/net/usb/sr9800.c +++ b/drivers/net/usb/sr9800.c @@ -470,14 +470,10 @@ static int sr_get_eeprom(struct net_device *net, static void sr_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) { - struct usbnet *dev = netdev_priv(net); - struct sr_data *data = (struct sr_data *)&dev->data; - /* Inherit standard device info */ usbnet_get_drvinfo(net, info); strncpy(info->driver, DRIVER_NAME, sizeof(info->driver)); strncpy(info->version, DRIVER_VERSION, sizeof(info->version)); - info->eedump_len = data->eeprom_len; } static u32 sr_get_link(struct net_device *net) diff --git a/drivers/net/vmxnet3/vmxnet3_ethtool.c b/drivers/net/vmxnet3/vmxnet3_ethtool.c index a681569..9ba11d7 100644 --- a/drivers/net/vmxnet3/vmxnet3_ethtool.c +++ b/drivers/net/vmxnet3/vmxnet3_ethtool.c @@ -214,10 +214,6 @@ vmxnet3_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->n_stats = vmxnet3_get_sset_count(netdev, ETH_SS_STATS); - drvinfo->testinfo_len = 0; - drvinfo->eedump_len = 0; - drvinfo->regdump_len = vmxnet3_get_regs_len(netdev); } diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c index 39f3e6f..ed0adaf 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -10470,7 +10470,6 @@ static void ipw_ethtool_get_drvinfo(struct net_device *dev, vers, date); strlcpy(info->bus_info, pci_name(p->pci_dev), sizeof(info->bus_info)); - info->eedump_len = IPW_EEPROM_IMAGE_SIZE; } static u32 ipw_ethtool_get_link(struct net_device *dev) -- cgit v0.10.2 From ef84d8ce5a36d0c4a6454e7e9dff54d19f96a25f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 14 Oct 2015 11:16:26 -0700 Subject: Revert "inet: fix double request socket freeing" This reverts commit c69736696cf3742b37d850289dc0d7ead177bb14. At the time of above commit, tcp_req_err() and dccp_req_err() were dead code, as SYN_RECV request sockets were not yet in ehash table. Real bug was fixed later in a different commit. We need to revert to not leak a refcount on request socket. inet_csk_reqsk_queue_drop_and_put() will be added in following commit to make clean inet_csk_reqsk_queue_drop() does not release the reference owned by caller. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 0dcf196..644af51 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -208,7 +208,6 @@ void dccp_req_err(struct sock *sk, u64 seq) if (!between48(seq, dccp_rsk(req)->dreq_iss, dccp_rsk(req)->dreq_gss)) { NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS); - reqsk_put(req); } else { /* * Still in RESPOND, just remove it silently. @@ -218,6 +217,7 @@ void dccp_req_err(struct sock *sk, u64 seq) */ inet_csk_reqsk_queue_drop(req->rsk_listener, req); } + reqsk_put(req); } EXPORT_SYMBOL(dccp_req_err); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 1ff0923..aad2298 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -324,7 +324,6 @@ void tcp_req_err(struct sock *sk, u32 seq) if (seq != tcp_rsk(req)->snt_isn) { NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS); - reqsk_put(req); } else { /* * Still in SYN_RECV, just remove it silently. @@ -332,9 +331,10 @@ void tcp_req_err(struct sock *sk, u32 seq) * created socket, and POSIX does not want network * errors returned from accept(). */ - NET_INC_STATS_BH(net, LINUX_MIB_LISTENDROPS); inet_csk_reqsk_queue_drop(req->rsk_listener, req); + NET_INC_STATS_BH(net, LINUX_MIB_LISTENDROPS); } + reqsk_put(req); } EXPORT_SYMBOL(tcp_req_err); -- cgit v0.10.2 From f03f2e154f52fdaa982de7e2c386737679963dc9 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 14 Oct 2015 11:16:27 -0700 Subject: tcp/dccp: add inet_csk_reqsk_queue_drop_and_put() helper Let's reduce the confusion about inet_csk_reqsk_queue_drop() : In many cases we also need to release reference on request socket, so add a helper to do this, reducing code size and complexity. Fixes: 4bdc3d66147b ("tcp/dccp: fix behavior of stale SYN_RECV request sockets") Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index fd645c4..e84ea9f 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h @@ -299,6 +299,7 @@ static inline int inet_csk_reqsk_queue_is_full(const struct sock *sk) } void inet_csk_reqsk_queue_drop(struct sock *sk, struct request_sock *req); +void inet_csk_reqsk_queue_drop_and_put(struct sock *sk, struct request_sock *req); void inet_csk_destroy_sock(struct sock *sk); void inet_csk_prepare_forced_close(struct sock *sk); diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 644af51..59bc180 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -828,7 +828,7 @@ lookup: if (likely(sk->sk_state == DCCP_LISTEN)) { nsk = dccp_check_req(sk, skb, req); } else { - inet_csk_reqsk_queue_drop(sk, req); + inet_csk_reqsk_queue_drop_and_put(sk, req); goto lookup; } if (!nsk) { diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 6883193..d9cc731f 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -686,7 +686,7 @@ lookup: if (likely(sk->sk_state == DCCP_LISTEN)) { nsk = dccp_check_req(sk, skb, req); } else { - inet_csk_reqsk_queue_drop(sk, req); + inet_csk_reqsk_queue_drop_and_put(sk, req); goto lookup; } if (!nsk) { diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index ba9ec9a..b85c720 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -546,6 +546,13 @@ void inet_csk_reqsk_queue_drop(struct sock *sk, struct request_sock *req) } EXPORT_SYMBOL(inet_csk_reqsk_queue_drop); +void inet_csk_reqsk_queue_drop_and_put(struct sock *sk, struct request_sock *req) +{ + inet_csk_reqsk_queue_drop(sk, req); + reqsk_put(req); +} +EXPORT_SYMBOL(inet_csk_reqsk_queue_drop_and_put); + static void reqsk_timer_handler(unsigned long data) { struct request_sock *req = (struct request_sock *)data; @@ -608,8 +615,7 @@ static void reqsk_timer_handler(unsigned long data) return; } drop: - inet_csk_reqsk_queue_drop(sk_listener, req); - reqsk_put(req); + inet_csk_reqsk_queue_drop_and_put(sk_listener, req); } static void reqsk_queue_hash_req(struct request_sock *req, diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index aad2298..9c68cf3 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1591,7 +1591,7 @@ process: if (likely(sk->sk_state == TCP_LISTEN)) { nsk = tcp_check_req(sk, skb, req, false); } else { - inet_csk_reqsk_queue_drop(sk, req); + inet_csk_reqsk_queue_drop_and_put(sk, req); goto lookup; } if (!nsk) { diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 7ce1c57..acb06f8 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1386,7 +1386,7 @@ process: if (likely(sk->sk_state == TCP_LISTEN)) { nsk = tcp_check_req(sk, skb, req, false); } else { - inet_csk_reqsk_queue_drop(sk, req); + inet_csk_reqsk_queue_drop_and_put(sk, req); goto lookup; } if (!nsk) { -- cgit v0.10.2 From ebb516af60e18258aac8e80bbe068740ef1579ed Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 14 Oct 2015 11:16:28 -0700 Subject: tcp/dccp: fix race at listener dismantle phase Under stress, a close() on a listener can trigger the WARN_ON(sk->sk_ack_backlog) in inet_csk_listen_stop() We need to test if listener is still active before queueing a child in inet_csk_reqsk_queue_add() Create a common inet_child_forget() helper, and use it from inet_csk_reqsk_queue_add() and inet_csk_listen_stop() Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index e84ea9f..6361570 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h @@ -268,13 +268,8 @@ struct dst_entry *inet_csk_route_child_sock(const struct sock *sk, struct sock *newsk, const struct request_sock *req); -static inline void inet_csk_reqsk_queue_add(struct sock *sk, - struct request_sock *req, - struct sock *child) -{ - reqsk_queue_add(&inet_csk(sk)->icsk_accept_queue, req, sk, child); -} - +void inet_csk_reqsk_queue_add(struct sock *sk, struct request_sock *req, + struct sock *child); void inet_csk_reqsk_queue_hash_add(struct sock *sk, struct request_sock *req, unsigned long timeout); diff --git a/include/net/request_sock.h b/include/net/request_sock.h index 2e73748..a0dde04 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -186,25 +186,6 @@ static inline bool reqsk_queue_empty(const struct request_sock_queue *queue) return queue->rskq_accept_head == NULL; } -static inline void reqsk_queue_add(struct request_sock_queue *queue, - struct request_sock *req, - struct sock *parent, - struct sock *child) -{ - spin_lock(&queue->rskq_lock); - req->sk = child; - sk_acceptq_added(parent); - - if (queue->rskq_accept_head == NULL) - queue->rskq_accept_head = req; - else - queue->rskq_accept_tail->dl_next = req; - - queue->rskq_accept_tail = req; - req->dl_next = NULL; - spin_unlock(&queue->rskq_lock); -} - static inline struct request_sock *reqsk_queue_remove(struct request_sock_queue *queue, struct sock *parent) { diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index b85c720..8430bc8 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -764,6 +764,53 @@ int inet_csk_listen_start(struct sock *sk, int backlog) } EXPORT_SYMBOL_GPL(inet_csk_listen_start); +static void inet_child_forget(struct sock *sk, struct request_sock *req, + struct sock *child) +{ + sk->sk_prot->disconnect(child, O_NONBLOCK); + + sock_orphan(child); + + percpu_counter_inc(sk->sk_prot->orphan_count); + + if (sk->sk_protocol == IPPROTO_TCP && tcp_rsk(req)->tfo_listener) { + BUG_ON(tcp_sk(child)->fastopen_rsk != req); + BUG_ON(sk != req->rsk_listener); + + /* Paranoid, to prevent race condition if + * an inbound pkt destined for child is + * blocked by sock lock in tcp_v4_rcv(). + * Also to satisfy an assertion in + * tcp_v4_destroy_sock(). + */ + tcp_sk(child)->fastopen_rsk = NULL; + } + inet_csk_destroy_sock(child); + reqsk_put(req); +} + +void inet_csk_reqsk_queue_add(struct sock *sk, struct request_sock *req, + struct sock *child) +{ + struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue; + + spin_lock(&queue->rskq_lock); + if (unlikely(sk->sk_state != TCP_LISTEN)) { + inet_child_forget(sk, req, child); + } else { + req->sk = child; + req->dl_next = NULL; + if (queue->rskq_accept_head == NULL) + queue->rskq_accept_head = req; + else + queue->rskq_accept_tail->dl_next = req; + queue->rskq_accept_tail = req; + sk_acceptq_added(sk); + } + spin_unlock(&queue->rskq_lock); +} +EXPORT_SYMBOL(inet_csk_reqsk_queue_add); + /* * This routine closes sockets which have been at least partially * opened, but not yet accepted. @@ -790,31 +837,11 @@ void inet_csk_listen_stop(struct sock *sk) WARN_ON(sock_owned_by_user(child)); sock_hold(child); - sk->sk_prot->disconnect(child, O_NONBLOCK); - - sock_orphan(child); - - percpu_counter_inc(sk->sk_prot->orphan_count); - - if (sk->sk_protocol == IPPROTO_TCP && tcp_rsk(req)->tfo_listener) { - BUG_ON(tcp_sk(child)->fastopen_rsk != req); - BUG_ON(sk != req->rsk_listener); - - /* Paranoid, to prevent race condition if - * an inbound pkt destined for child is - * blocked by sock lock in tcp_v4_rcv(). - * Also to satisfy an assertion in - * tcp_v4_destroy_sock(). - */ - tcp_sk(child)->fastopen_rsk = NULL; - } - inet_csk_destroy_sock(child); - + inet_child_forget(sk, req, child); bh_unlock_sock(child); local_bh_enable(); sock_put(child); - reqsk_put(req); cond_resched(); } if (queue->fastopenq.rskq_rst_head) { @@ -829,7 +856,7 @@ void inet_csk_listen_stop(struct sock *sk) req = next; } } - WARN_ON(sk->sk_ack_backlog); + WARN_ON_ONCE(sk->sk_ack_backlog); } EXPORT_SYMBOL_GPL(inet_csk_listen_stop); -- cgit v0.10.2 From ac00737f4e8198f8ff5007c70af4dfe4fd47ea94 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Wed, 14 Oct 2015 14:40:44 -0700 Subject: bpf: Need to call bpf_prog_uncharge_memlock from bpf_prog_put Currently, is only called from __prog_put_rcu in the bpf_prog_release path. Need this to call this from bpf_prog_put also to get correct accounting. Fixes: aaac3ba95e4c8b49 ("bpf: charge user for creation of BPF maps and programs") Signed-off-by: Tom Herbert Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index f640e5f..687dd6c 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -520,6 +520,7 @@ void bpf_prog_put(struct bpf_prog *prog) { if (atomic_dec_and_test(&prog->aux->refcnt)) { free_used_maps(prog->aux); + bpf_prog_uncharge_memlock(prog); bpf_prog_free(prog); } } -- cgit v0.10.2 From 51161aa98d0aa4eb20952e16d6c6dbb1d085330e Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 14 Oct 2015 16:44:00 -0700 Subject: net: Fix suspicious RCU usage in fib_rebalance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This command: ip route add 192.168.1.0/24 nexthop via 10.2.1.5 dev eth1 nexthop via 10.2.2.5 dev eth2 generated this suspicious RCU usage message: [ 63.249262] [ 63.249939] =============================== [ 63.251571] [ INFO: suspicious RCU usage. ] [ 63.253250] 4.3.0-rc3+ #298 Not tainted [ 63.254724] ------------------------------- [ 63.256401] ../include/linux/inetdevice.h:205 suspicious rcu_dereference_check() usage! [ 63.259450] [ 63.259450] other info that might help us debug this: [ 63.259450] [ 63.262297] [ 63.262297] rcu_scheduler_active = 1, debug_locks = 1 [ 63.264647] 1 lock held by ip/2870: [ 63.265896] #0: (rtnl_mutex){+.+.+.}, at: [] rtnl_lock+0x12/0x14 [ 63.268858] [ 63.268858] stack backtrace: [ 63.270409] CPU: 4 PID: 2870 Comm: ip Not tainted 4.3.0-rc3+ #298 [ 63.272478] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.7.5-20140531_083030-gandalf 04/01/2014 [ 63.275745] 0000000000000001 ffff8800b8c9f8b8 ffffffff8125f73c ffff88013afcf301 [ 63.278185] ffff8800bab7a380 ffff8800b8c9f8e8 ffffffff8107bf30 ffff8800bb728000 [ 63.280634] ffff880139fe9a60 0000000000000000 ffff880139fe9a00 ffff8800b8c9f908 [ 63.283177] Call Trace: [ 63.283959] [] dump_stack+0x4c/0x68 [ 63.285593] [] lockdep_rcu_suspicious+0xfa/0x103 [ 63.287500] [] __in_dev_get_rcu+0x48/0x4f [ 63.289169] [] fib_rebalance+0x3e/0x127 [ 63.290753] [] ? rcu_read_unlock+0x3e/0x5f [ 63.292442] [] fib_create_info+0xaf9/0xdcc [ 63.294093] [] ? sched_clock_local+0x12/0x75 [ 63.295791] [] fib_table_insert+0x8c/0x451 [ 63.297493] [] ? fib_get_table+0x36/0x43 [ 63.299109] [] inet_rtm_newroute+0x43/0x51 [ 63.300709] [] rtnetlink_rcv_msg+0x182/0x195 [ 63.302334] [] ? trace_hardirqs_on+0xd/0xf [ 63.303888] [] ? rtnl_lock+0x12/0x14 [ 63.305346] [] ? __rtnl_unlock+0x12/0x12 [ 63.306878] [] netlink_rcv_skb+0x3d/0x90 [ 63.308437] [] rtnetlink_rcv+0x21/0x28 [ 63.309916] [] netlink_unicast+0xfa/0x17f [ 63.311447] [] netlink_sendmsg+0x297/0x2dc [ 63.313029] [] sock_sendmsg_nosec+0x12/0x1d [ 63.314597] [] ___sys_sendmsg+0x196/0x21b [ 63.316125] [] ? native_sched_clock+0x1f/0x3c [ 63.317671] [] ? sched_clock_local+0x12/0x75 [ 63.319185] [] ? sched_clock_cpu+0x9d/0xb6 [ 63.320693] [] ? __lock_is_held+0x32/0x54 [ 63.322145] [] ? __fget_light+0x4b/0x77 [ 63.323541] [] __sys_sendmsg+0x3d/0x5b [ 63.324947] [] SyS_sendmsg+0xd/0x19 [ 63.326274] [] entry_SYSCALL_64_fastpath+0x12/0x6f It looks like all of the code paths to fib_rebalance are under rtnl. Fixes: 0e884c78ee19 ("ipv4: L3 hash-based multipath") Cc: Peter Nørlund Signed-off-by: David Ahern Signed-off-by: David S. Miller diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index af77298..42778d9 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -545,7 +545,7 @@ static void fib_rebalance(struct fib_info *fi) if (nh->nh_flags & RTNH_F_DEAD) continue; - in_dev = __in_dev_get_rcu(nh->nh_dev); + in_dev = __in_dev_get_rtnl(nh->nh_dev); if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) && @@ -559,7 +559,7 @@ static void fib_rebalance(struct fib_info *fi) change_nexthops(fi) { int upper_bound; - in_dev = __in_dev_get_rcu(nexthop_nh->nh_dev); + in_dev = __in_dev_get_rtnl(nexthop_nh->nh_dev); if (nexthop_nh->nh_flags & RTNH_F_DEAD) { upper_bound = -1; -- cgit v0.10.2 From c67caceb864cf15731532ab25162166c228fa270 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Thu, 24 Sep 2015 09:04:26 -0700 Subject: i40e/i40evf: Fix handling of napi budget The polling routine for i40e was rounding up the budget for Rx cleanup to 1. This is incorrect as the netpoll poll call is expecting no Rx to be processed as the budget passed was 0. Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 01e5ece..b560e02 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1846,6 +1846,10 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) ring->arm_wb = false; } + /* Handle case where we are called by netpoll with a budget of 0 */ + if (budget <= 0) + goto tx_only; + /* We attempt to distribute budget to each Rx queue fairly, but don't * allow the budget to go below 1 because that would exit polling early. */ @@ -1862,6 +1866,7 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) /* If work not completed, return budget and polling will return */ if (!clean_complete) { +tx_only: if (arm_wb) i40e_force_wb(vsi, q_vector); return budget; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 0e71eb4..1894cc8 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -1287,6 +1287,10 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget) ring->arm_wb = false; } + /* Handle case where we are called by netpoll with a budget of 0 */ + if (budget <= 0) + goto tx_only; + /* We attempt to distribute budget to each Rx queue fairly, but don't * allow the budget to go below 1 because that would exit polling early. */ @@ -1303,6 +1307,7 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget) /* If work not completed, return budget and polling will return */ if (!clean_complete) { +tx_only: if (arm_wb) i40evf_force_wb(vsi, q_vector); return budget; -- cgit v0.10.2 From 8b6503590510fd7a8b303feeaf8a45a192e2b8df Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Thu, 24 Sep 2015 09:04:32 -0700 Subject: i40e/i40evf: Drop useless "IN_NETPOLL" flag The code in i40e and i40evf is using an "IN_NETPOLL" flag that has never added any value due to the fact that the Rx clean-up is handled in NAPI. As such the flag was set, the queue was scheduled via NAPI, and then polled from the netpoll controller and if any Rx packets were processed the were processed in the wrong context. In addition the flag itself just added an unneeded conditional to the hot-path so it can safely be dropped and save us a few instructions. Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index d587a05..91f8cbf 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -307,7 +307,6 @@ struct i40e_pf { #ifdef I40E_FCOE #define I40E_FLAG_FCOE_ENABLED BIT_ULL(11) #endif /* I40E_FCOE */ -#define I40E_FLAG_IN_NETPOLL BIT_ULL(12) #define I40E_FLAG_16BYTE_RX_DESC_ENABLED BIT_ULL(13) #define I40E_FLAG_CLEAN_ADMINQ BIT_ULL(14) #define I40E_FLAG_FILTER_SYNC BIT_ULL(15) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 5ed844b..389f110 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -3574,14 +3574,12 @@ static void i40e_netpoll(struct net_device *netdev) if (test_bit(__I40E_DOWN, &vsi->state)) return; - pf->flags |= I40E_FLAG_IN_NETPOLL; if (pf->flags & I40E_FLAG_MSIX_ENABLED) { for (i = 0; i < vsi->num_q_vectors; i++) i40e_msix_clean_rings(0, vsi->q_vectors[i]); } else { i40e_intr(pf->pdev->irq, netdev); } - pf->flags &= ~I40E_FLAG_IN_NETPOLL; } #endif diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index b560e02..e1fac3b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1268,16 +1268,11 @@ static void i40e_receive_skb(struct i40e_ring *rx_ring, struct sk_buff *skb, u16 vlan_tag) { struct i40e_q_vector *q_vector = rx_ring->q_vector; - struct i40e_vsi *vsi = rx_ring->vsi; - u64 flags = vsi->back->flags; if (vlan_tag & VLAN_VID_MASK) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag); - if (flags & I40E_FLAG_IN_NETPOLL) - netif_rx(skb); - else - napi_gro_receive(&q_vector->napi, skb); + napi_gro_receive(&q_vector->napi, skb); } /** diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 1894cc8..5d3a8bd 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -742,16 +742,11 @@ static void i40e_receive_skb(struct i40e_ring *rx_ring, struct sk_buff *skb, u16 vlan_tag) { struct i40e_q_vector *q_vector = rx_ring->q_vector; - struct i40e_vsi *vsi = rx_ring->vsi; - u64 flags = vsi->back->flags; if (vlan_tag & VLAN_VID_MASK) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag); - if (flags & I40E_FLAG_IN_NETPOLL) - netif_rx(skb); - else - napi_gro_receive(&q_vector->napi, skb); + napi_gro_receive(&q_vector->napi, skb); } /** diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index 6f42711..c74f614 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -211,7 +211,6 @@ struct i40evf_adapter { #define I40EVF_FLAG_RX_1BUF_CAPABLE BIT(1) #define I40EVF_FLAG_RX_PS_CAPABLE BIT(2) #define I40EVF_FLAG_RX_PS_ENABLED BIT(3) -#define I40EVF_FLAG_IN_NETPOLL BIT(4) #define I40EVF_FLAG_IMIR_ENABLED BIT(5) #define I40EVF_FLAG_MQ_CAPABLE BIT(6) #define I40EVF_FLAG_NEED_LINK_UPDATE BIT(7) @@ -224,7 +223,6 @@ struct i40evf_adapter { /* duplicates for common code */ #define I40E_FLAG_FDIR_ATR_ENABLED 0 #define I40E_FLAG_DCB_ENABLED 0 -#define I40E_FLAG_IN_NETPOLL I40EVF_FLAG_IN_NETPOLL #define I40E_FLAG_RX_CSUM_ENABLED I40EVF_FLAG_RX_CSUM_ENABLED #define I40E_FLAG_WB_ON_ITR_CAPABLE I40EVF_FLAG_WB_ON_ITR_CAPABLE #define I40E_FLAG_OUTER_UDP_CSUM_CAPABLE I40EVF_FLAG_OUTER_UDP_CSUM_CAPABLE -- cgit v0.10.2 From 7709b4c1fff39972b6a1b6183b593c43f1a555fb Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Thu, 24 Sep 2015 09:04:38 -0700 Subject: i40evf: Add support for netpoll Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 429a46c..139801f 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -444,6 +444,29 @@ out: return err; } +#ifdef CONFIG_NET_POLL_CONTROLLER +/** + * i40evf_netpoll - A Polling 'interrupt' handler + * @netdev: network interface device structure + * + * This is used by netconsole to send skbs without having to re-enable + * interrupts. It's not called while the normal interrupt routine is executing. + **/ +static void i40evf_netpoll(struct net_device *netdev) +{ + struct i40evf_adapter *adapter = netdev_priv(netdev); + int q_vectors = adapter->num_msix_vectors - NONQ_VECS; + int i; + + /* if interface is down do nothing */ + if (test_bit(__I40E_DOWN, &adapter->vsi.state)) + return; + + for (i = 0; i < q_vectors; i++) + i40evf_msix_clean_rings(0, adapter->q_vector[i]); +} + +#endif /** * i40evf_request_traffic_irqs - Initialize MSI-X interrupts * @adapter: board private structure @@ -2049,6 +2072,9 @@ 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, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = i40evf_netpoll, +#endif }; /** -- cgit v0.10.2 From 32b3e08fff60494cd1d281a39b51583edfd2b18f Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Thu, 24 Sep 2015 16:35:47 -0700 Subject: drivers/net/intel: use napi_complete_done() As per Eric Dumazet's previous patches: (see commit (24d2e4a50737) - tg3: use napi_complete_done()) Quoting verbatim: Using napi_complete_done() instead of napi_complete() allows us to use /sys/class/net/ethX/gro_flush_timeout GRO layer can aggregate more packets if the flush is delayed a bit, without having to set too big coalescing parameters that impact latencies. Tested configuration: low latency via ethtool -C ethx adaptive-rx off rx-usecs 10 adaptive-tx off tx-usecs 15 workload: streaming rx using netperf TCP_MAERTS igb: MIGRATED TCP MAERTS TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.0.0.1 () port 0 AF_INET : demo ... Interim result: 941.48 10^6bits/s over 1.000 seconds ending at 1440193171.589 Alignment Offset Bytes Bytes Recvs Bytes Sends Local Remote Local Remote Xfered Per Per Recv Send Recv Send Recv (avg) Send (avg) 8 8 0 0 1176930056 1475.36 797726 16384.00 71905 MIGRATED TCP MAERTS TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.0.0.1 () port 0 AF_INET : demo ... Interim result: 941.49 10^6bits/s over 0.997 seconds ending at 1440193142.763 Alignment Offset Bytes Bytes Recvs Bytes Sends Local Remote Local Remote Xfered Per Per Recv Send Recv Send Recv (avg) Send (avg) 8 8 0 0 1175182320 50476.00 23282 16384.00 71816 i40e: Hard to test because the traffic is incoming so fast (24Gb/s) that GRO always receives 87kB, even at the highest interrupt rate. Other drivers were only compile tested. Signed-off-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c index 74dc150..fd7be86 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_main.c +++ b/drivers/net/ethernet/intel/e1000/e1000_main.c @@ -3820,7 +3820,7 @@ static int e1000_clean(struct napi_struct *napi, int budget) if (work_done < budget) { if (likely(adapter->itr_setting & 3)) e1000_set_itr(adapter); - napi_complete(napi); + napi_complete_done(napi, work_done); if (!test_bit(__E1000_DOWN, &adapter->flags)) e1000_irq_enable(adapter); } diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 2e2ddec0..0a854a4 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -2693,7 +2693,7 @@ static int e1000e_poll(struct napi_struct *napi, int weight) if (work_done < weight) { if (adapter->itr_setting & 3) e1000_set_itr(adapter); - napi_complete(napi); + napi_complete_done(napi, work_done); if (!test_bit(__E1000_DOWN, &adapter->state)) { if (adapter->msix_entries) ew32(IMS, adapter->rx_ring->ims_val); diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_main.c b/drivers/net/ethernet/intel/fm10k/fm10k_main.c index 537d81a..e76a44c 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_main.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_main.c @@ -593,9 +593,9 @@ static void fm10k_receive_skb(struct fm10k_q_vector *q_vector, napi_gro_receive(&q_vector->napi, skb); } -static bool fm10k_clean_rx_irq(struct fm10k_q_vector *q_vector, - struct fm10k_ring *rx_ring, - int budget) +static int fm10k_clean_rx_irq(struct fm10k_q_vector *q_vector, + struct fm10k_ring *rx_ring, + int budget) { struct sk_buff *skb = rx_ring->skb; unsigned int total_bytes = 0, total_packets = 0; @@ -662,7 +662,7 @@ static bool fm10k_clean_rx_irq(struct fm10k_q_vector *q_vector, q_vector->rx.total_packets += total_packets; q_vector->rx.total_bytes += total_bytes; - return total_packets < budget; + return total_packets; } #define VXLAN_HLEN (sizeof(struct udphdr) + 8) @@ -1422,7 +1422,7 @@ static int fm10k_poll(struct napi_struct *napi, int budget) struct fm10k_q_vector *q_vector = container_of(napi, struct fm10k_q_vector, napi); struct fm10k_ring *ring; - int per_ring_budget; + int per_ring_budget, work_done = 0; bool clean_complete = true; fm10k_for_each_ring(ring, q_vector->tx) @@ -1436,16 +1436,19 @@ static int fm10k_poll(struct napi_struct *napi, int budget) else per_ring_budget = budget; - fm10k_for_each_ring(ring, q_vector->rx) - clean_complete &= fm10k_clean_rx_irq(q_vector, ring, - per_ring_budget); + fm10k_for_each_ring(ring, q_vector->rx) { + int work = fm10k_clean_rx_irq(q_vector, ring, per_ring_budget); + + work_done += work; + clean_complete &= !!(work < per_ring_budget); + } /* If all work not completed, return budget and keep polling */ if (!clean_complete) return budget; /* all work done, exit the polling mode */ - napi_complete(napi); + napi_complete_done(napi, work_done); /* re-enable the q_vector */ fm10k_qv_enable(q_vector); diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index e1fac3b..512707c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1825,7 +1825,7 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) bool clean_complete = true; bool arm_wb = false; int budget_per_ring; - int cleaned; + int work_done = 0; if (test_bit(__I40E_DOWN, &vsi->state)) { napi_complete(napi); @@ -1851,10 +1851,14 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) budget_per_ring = max(budget/q_vector->num_ringpairs, 1); i40e_for_each_ring(ring, q_vector->rx) { + int cleaned; + if (ring_is_ps_enabled(ring)) cleaned = i40e_clean_rx_irq_ps(ring, budget_per_ring); else cleaned = i40e_clean_rx_irq_1buf(ring, budget_per_ring); + + work_done += cleaned; /* if we didn't clean as many as budgeted, we must be done */ clean_complete &= (budget_per_ring != cleaned); } @@ -1871,7 +1875,7 @@ tx_only: q_vector->arm_wb_state = false; /* Work is done so exit the polling mode and re-enable the interrupt */ - napi_complete(napi); + napi_complete_done(napi, work_done); if (vsi->back->flags & I40E_FLAG_MSIX_ENABLED) { i40e_update_enable_itr(vsi, q_vector); } else { /* Legacy mode */ diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 5d3a8bd..97493a4 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -1266,7 +1266,7 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget) bool clean_complete = true; bool arm_wb = false; int budget_per_ring; - int cleaned; + int work_done = 0; if (test_bit(__I40E_DOWN, &vsi->state)) { napi_complete(napi); @@ -1292,10 +1292,14 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget) budget_per_ring = max(budget/q_vector->num_ringpairs, 1); i40e_for_each_ring(ring, q_vector->rx) { + int cleaned; + if (ring_is_ps_enabled(ring)) cleaned = i40e_clean_rx_irq_ps(ring, budget_per_ring); else cleaned = i40e_clean_rx_irq_1buf(ring, budget_per_ring); + + work_done += cleaned; /* if we didn't clean as many as budgeted, we must be done */ clean_complete &= (budget_per_ring != cleaned); } @@ -1312,7 +1316,7 @@ tx_only: q_vector->arm_wb_state = false; /* Work is done so exit the polling mode and re-enable the interrupt */ - napi_complete(napi); + napi_complete_done(napi, work_done); i40e_update_enable_itr(vsi, q_vector); return 0; } diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 7e62675..ea7b098 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -151,7 +151,7 @@ static void igb_setup_dca(struct igb_adapter *); #endif /* CONFIG_IGB_DCA */ static int igb_poll(struct napi_struct *, int); static bool igb_clean_tx_irq(struct igb_q_vector *); -static bool igb_clean_rx_irq(struct igb_q_vector *, int); +static int igb_clean_rx_irq(struct igb_q_vector *, int); static int igb_ioctl(struct net_device *, struct ifreq *, int cmd); static void igb_tx_timeout(struct net_device *); static void igb_reset_task(struct work_struct *); @@ -6364,6 +6364,7 @@ static int igb_poll(struct napi_struct *napi, int budget) struct igb_q_vector, napi); bool clean_complete = true; + int work_done = 0; #ifdef CONFIG_IGB_DCA if (q_vector->adapter->flags & IGB_FLAG_DCA_ENABLED) @@ -6372,15 +6373,19 @@ static int igb_poll(struct napi_struct *napi, int budget) if (q_vector->tx.ring) clean_complete = igb_clean_tx_irq(q_vector); - if (q_vector->rx.ring) - clean_complete &= igb_clean_rx_irq(q_vector, budget); + if (q_vector->rx.ring) { + int cleaned = igb_clean_rx_irq(q_vector, budget); + + work_done += cleaned; + clean_complete &= (cleaned < budget); + } /* If all work not completed, return budget and keep polling */ if (!clean_complete) return budget; /* If not enough Rx work done, exit the polling mode */ - napi_complete(napi); + napi_complete_done(napi, work_done); igb_ring_irq_enable(q_vector); return 0; @@ -6904,7 +6909,7 @@ static void igb_process_skb_fields(struct igb_ring *rx_ring, skb->protocol = eth_type_trans(skb, rx_ring->netdev); } -static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget) +static int igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget) { struct igb_ring *rx_ring = q_vector->rx.ring; struct sk_buff *skb = rx_ring->skb; @@ -6978,7 +6983,7 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget) if (cleaned_count) igb_alloc_rx_buffers(rx_ring, cleaned_count); - return total_packets < budget; + return total_packets; } static bool igb_alloc_mapped_page(struct igb_ring *rx_ring, diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c index e86d41e..297af80 100644 --- a/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/drivers/net/ethernet/intel/igbvf/netdev.c @@ -1211,7 +1211,7 @@ static int igbvf_poll(struct napi_struct *napi, int budget) /* If not enough Rx work done, exit the polling mode */ if (work_done < budget) { - napi_complete(napi); + napi_complete_done(napi, work_done); if (adapter->requested_itr & 3) igbvf_set_itr(adapter); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 1b2ad39..9f8a7fd 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -2775,7 +2775,7 @@ int ixgbe_poll(struct napi_struct *napi, int budget) container_of(napi, struct ixgbe_q_vector, napi); struct ixgbe_adapter *adapter = q_vector->adapter; struct ixgbe_ring *ring; - int per_ring_budget; + int per_ring_budget, work_done = 0; bool clean_complete = true; #ifdef CONFIG_IXGBE_DCA @@ -2796,9 +2796,13 @@ int ixgbe_poll(struct napi_struct *napi, int budget) else per_ring_budget = budget; - ixgbe_for_each_ring(ring, q_vector->rx) - clean_complete &= (ixgbe_clean_rx_irq(q_vector, ring, - per_ring_budget) < per_ring_budget); + ixgbe_for_each_ring(ring, q_vector->rx) { + int cleaned = ixgbe_clean_rx_irq(q_vector, ring, + per_ring_budget); + + work_done += cleaned; + clean_complete &= (cleaned < per_ring_budget); + } ixgbe_qv_unlock_napi(q_vector); /* If all work not completed, return budget and keep polling */ @@ -2806,7 +2810,7 @@ int ixgbe_poll(struct napi_struct *napi, int budget) return budget; /* all work done, exit the polling mode */ - napi_complete(napi); + napi_complete_done(napi, work_done); if (adapter->rx_itr_setting & 1) ixgbe_set_itr(q_vector); if (!test_bit(__IXGBE_DOWN, &adapter->state)) diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 35da2d7..7570b5c 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -1008,7 +1008,7 @@ static int ixgbevf_poll(struct napi_struct *napi, int budget) container_of(napi, struct ixgbevf_q_vector, napi); struct ixgbevf_adapter *adapter = q_vector->adapter; struct ixgbevf_ring *ring; - int per_ring_budget; + int per_ring_budget, work_done = 0; bool clean_complete = true; ixgbevf_for_each_ring(ring, q_vector->tx) @@ -1027,10 +1027,12 @@ static int ixgbevf_poll(struct napi_struct *napi, int budget) else per_ring_budget = budget; - ixgbevf_for_each_ring(ring, q_vector->rx) - clean_complete &= (ixgbevf_clean_rx_irq(q_vector, ring, - per_ring_budget) - < per_ring_budget); + ixgbevf_for_each_ring(ring, q_vector->rx) { + int cleaned = ixgbevf_clean_rx_irq(q_vector, ring, + per_ring_budget); + work_done += cleaned; + clean_complete &= (cleaned < per_ring_budget); + } #ifdef CONFIG_NET_RX_BUSY_POLL ixgbevf_qv_unlock_napi(q_vector); @@ -1040,7 +1042,7 @@ static int ixgbevf_poll(struct napi_struct *napi, int budget) if (!clean_complete) return budget; /* all work done, exit the polling mode */ - napi_complete(napi); + napi_complete_done(napi, work_done); if (adapter->rx_itr_setting & 1) ixgbevf_set_itr(q_vector); if (!test_bit(__IXGBEVF_DOWN, &adapter->state) && -- cgit v0.10.2 From 6dec101765e442fc7f46205acdb2cf3b88879f16 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Mon, 28 Sep 2015 14:12:30 -0400 Subject: i40e: generate fewer startup messages Cut down on the number of startup log entries by putting a couple behind debug flags and combining a couple others into a single line. Change-ID: I708089f086308f84d43f8b6f0e8a634a02d058fb Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 91f8cbf..7aaead3 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -582,10 +582,10 @@ struct i40e_device { }; /** - * i40e_fw_version_str - format the FW and NVM version strings + * i40e_nvm_version_str - format the NVM version strings * @hw: ptr to the hardware info **/ -static inline char *i40e_fw_version_str(struct i40e_hw *hw) +static inline char *i40e_nvm_version_str(struct i40e_hw *hw) { static char buf[32]; diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index e7fd84c..e994f4a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -1122,7 +1122,7 @@ static void i40e_get_drvinfo(struct net_device *netdev, strlcpy(drvinfo->driver, i40e_driver_name, sizeof(drvinfo->driver)); strlcpy(drvinfo->version, i40e_driver_version_str, sizeof(drvinfo->version)); - strlcpy(drvinfo->fw_version, i40e_fw_version_str(&pf->hw), + strlcpy(drvinfo->fw_version, i40e_nvm_version_str(&pf->hw), sizeof(drvinfo->fw_version)); strlcpy(drvinfo->bus_info, pci_name(pf->pdev), sizeof(drvinfo->bus_info)); diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 389f110..d731095 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -6218,8 +6218,9 @@ static void i40e_config_bridge_mode(struct i40e_veb *veb) { struct i40e_pf *pf = veb->pf; - dev_info(&pf->pdev->dev, "enabling bridge mode: %s\n", - veb->bridge_mode == BRIDGE_MODE_VEPA ? "VEPA" : "VEB"); + if (pf->hw.debug_mask & I40E_DEBUG_LAN) + dev_info(&pf->pdev->dev, "enabling bridge mode: %s\n", + veb->bridge_mode == BRIDGE_MODE_VEPA ? "VEPA" : "VEB"); if (veb->bridge_mode & BRIDGE_MODE_VEPA) i40e_disable_pf_switch_lb(pf); else @@ -9923,6 +9924,10 @@ static void i40e_print_features(struct i40e_pf *pf) if (pf->flags & I40E_FLAG_FCOE_ENABLED) buf += sprintf(buf, "FCOE "); #endif + if (pf->flags & I40E_FLAG_VEB_MODE_ENABLED) + buf += sprintf(buf, "VEB "); + else + buf += sprintf(buf, "VEPA "); BUG_ON(buf > (string + INFO_STRING_LEN)); dev_info(&pf->pdev->dev, "%s\n", string); @@ -10062,13 +10067,12 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pf->hw.fc.requested_mode = I40E_FC_NONE; err = i40e_init_adminq(hw); - dev_info(&pdev->dev, "%s\n", i40e_fw_version_str(hw)); - /* provide additional fw info, like api and ver */ - dev_info(&pdev->dev, "fw_version:%d.%d.%05d\n", - hw->aq.fw_maj_ver, hw->aq.fw_min_ver, hw->aq.fw_build); - dev_info(&pdev->dev, "fw api version:%d.%d\n", - hw->aq.api_maj_ver, hw->aq.api_min_ver); + /* provide nvm, fw, api versions */ + dev_info(&pdev->dev, "fw %d.%d.%05d api %d.%d nvm %s\n", + hw->aq.fw_maj_ver, hw->aq.fw_min_ver, hw->aq.fw_build, + hw->aq.api_maj_ver, hw->aq.api_min_ver, + i40e_nvm_version_str(hw)); if (err) { dev_info(&pdev->dev, diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index 552c84e..565ca7c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -674,8 +674,8 @@ void i40e_ptp_init(struct i40e_pf *pf) struct timespec64 ts; u32 regval; - dev_info(&pf->pdev->dev, "%s: added PHC on %s\n", __func__, - netdev->name); + if (pf->hw.debug_mask & I40E_DEBUG_LAN) + dev_info(&pf->pdev->dev, "PHC enabled\n"); pf->flags |= I40E_FLAG_PTP; /* Ensure the clocks are running. */ -- cgit v0.10.2 From 4e68adfeb91d684e5c7a0c6305af872120c71f23 Mon Sep 17 00:00:00 2001 From: Jingjing Wu Date: Mon, 28 Sep 2015 14:12:31 -0400 Subject: i40e/i40evf: Fix compile issue related to const string Add const to functions that return strings that aren't going to be modified. This addresses some reported compile complaints. Change-ID: Ic56b1e814ab4d23a50480e7fdec652445f776ee8 Signed-off-by: Jingjing Wu Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index d1ee174..daa6b17 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -87,7 +87,7 @@ static i40e_status i40e_set_mac_type(struct i40e_hw *hw) * @hw: pointer to the HW structure * @aq_err: the AQ error code to convert **/ -char *i40e_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err) +const char *i40e_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err) { switch (aq_err) { case I40E_AQ_RC_OK: @@ -147,7 +147,7 @@ char *i40e_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err) * @hw: pointer to the HW structure * @stat_err: the status error code to convert **/ -char *i40e_stat_str(struct i40e_hw *hw, i40e_status stat_err) +const char *i40e_stat_str(struct i40e_hw *hw, i40e_status stat_err) { switch (stat_err) { case 0: diff --git a/drivers/net/ethernet/intel/i40e/i40e_nvm.c b/drivers/net/ethernet/intel/i40e/i40e_nvm.c index 2142e10..875a8cc 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_nvm.c +++ b/drivers/net/ethernet/intel/i40e/i40e_nvm.c @@ -632,7 +632,7 @@ static inline u8 i40e_nvmupd_get_transaction(u32 val) return (u8)((val & I40E_NVM_TRANS_MASK) >> I40E_NVM_TRANS_SHIFT); } -static char *i40e_nvm_update_state_str[] = { +static const char * const i40e_nvm_update_state_str[] = { "I40E_NVMUPD_INVALID", "I40E_NVMUPD_READ_CON", "I40E_NVMUPD_READ_SNT", diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h index 4454974..10bf2ba 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h +++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h @@ -58,8 +58,8 @@ void i40e_debug_aq(struct i40e_hw *hw, enum i40e_debug_mask mask, void i40e_idle_aq(struct i40e_hw *hw); bool i40e_check_asq_alive(struct i40e_hw *hw); i40e_status i40e_aq_queue_shutdown(struct i40e_hw *hw, bool unloading); -char *i40e_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err); -char *i40e_stat_str(struct i40e_hw *hw, i40e_status stat_err); +const char *i40e_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err); +const char *i40e_stat_str(struct i40e_hw *hw, i40e_status stat_err); i40e_status i40e_aq_get_rss_lut(struct i40e_hw *hw, u16 seid, bool pf_lut, u8 *lut, u16 lut_size); diff --git a/drivers/net/ethernet/intel/i40evf/i40e_common.c b/drivers/net/ethernet/intel/i40evf/i40e_common.c index b98b642..7435fb3 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_common.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_common.c @@ -87,7 +87,7 @@ i40e_status i40e_set_mac_type(struct i40e_hw *hw) * @hw: pointer to the HW structure * @aq_err: the AQ error code to convert **/ -char *i40evf_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err) +const char *i40evf_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err) { switch (aq_err) { case I40E_AQ_RC_OK: @@ -147,7 +147,7 @@ char *i40evf_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err) * @hw: pointer to the HW structure * @stat_err: the status error code to convert **/ -char *i40evf_stat_str(struct i40e_hw *hw, i40e_status stat_err) +const char *i40evf_stat_str(struct i40e_hw *hw, i40e_status stat_err) { switch (stat_err) { case 0: diff --git a/drivers/net/ethernet/intel/i40evf/i40e_prototype.h b/drivers/net/ethernet/intel/i40evf/i40e_prototype.h index 55ae4b0..8ed0edf 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_prototype.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_prototype.h @@ -60,8 +60,8 @@ void i40e_idle_aq(struct i40e_hw *hw); void i40evf_resume_aq(struct i40e_hw *hw); bool i40evf_check_asq_alive(struct i40e_hw *hw); i40e_status i40evf_aq_queue_shutdown(struct i40e_hw *hw, bool unloading); -char *i40evf_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err); -char *i40evf_stat_str(struct i40e_hw *hw, i40e_status stat_err); +const char *i40evf_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err); +const char *i40evf_stat_str(struct i40e_hw *hw, i40e_status stat_err); i40e_status i40evf_aq_get_rss_lut(struct i40e_hw *hw, u16 seid, bool pf_lut, u8 *lut, u16 lut_size); -- cgit v0.10.2 From 3c5c420535ab7e25a639db16f7b2b16a70e147ec Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Mon, 28 Sep 2015 14:12:32 -0400 Subject: i40e: remove read/write failed messages from nvmupdate Allow the nvmupdate application to decide when a read or write error should be exposed to the user. Since the application needs to use write probes to find the ReadOnly sections on a potentially unknown NVM version in the HW and read probes to check the status of the last write, some error messages are expected, but need not be shown to the users. The driver doesn't know which are ignorable from real errors, so needs to let the application make the decision. Change-ID: I78fca8ab672bede11c10c820b83c26adfd536d03 Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index e994f4a..5fe8ac0 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -993,9 +993,7 @@ static int i40e_get_eeprom(struct net_device *netdev, cmd = (struct i40e_nvm_access *)eeprom; ret_val = i40e_nvmupd_command(hw, cmd, bytes, &errno); - if (ret_val && - ((hw->aq.asq_last_status != I40E_AQ_RC_EACCES) || - (hw->debug_mask & I40E_DEBUG_NVM))) + if (ret_val && (hw->debug_mask & I40E_DEBUG_NVM)) dev_info(&pf->pdev->dev, "NVMUpdate read failed err=%d status=0x%x errno=%d module=%d offset=0x%x size=%d\n", ret_val, hw->aq.asq_last_status, errno, @@ -1099,10 +1097,7 @@ static int i40e_set_eeprom(struct net_device *netdev, cmd = (struct i40e_nvm_access *)eeprom; ret_val = i40e_nvmupd_command(hw, cmd, bytes, &errno); - if (ret_val && - ((hw->aq.asq_last_status != I40E_AQ_RC_EPERM && - hw->aq.asq_last_status != I40E_AQ_RC_EBUSY) || - (hw->debug_mask & I40E_DEBUG_NVM))) + if (ret_val && (hw->debug_mask & I40E_DEBUG_NVM)) dev_info(&pf->pdev->dev, "NVMUpdate write failed err=%d status=0x%x errno=%d module=%d offset=0x%x size=%d\n", ret_val, hw->aq.asq_last_status, errno, -- cgit v0.10.2 From d9d17cf74aed93add76c9cf648ce2f73b68ce410 Mon Sep 17 00:00:00 2001 From: Greg Rose Date: Mon, 28 Sep 2015 14:12:33 -0400 Subject: i40e: Removed unused defines Two defines that are not used are causing customer confusion - remove them. Change-ID: Icef0325aca8e0f4fcdfc519e026bdd375e791200 Signed-off-by: Greg Rose Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h index 736f6f0..7fe94e8 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h @@ -29,8 +29,6 @@ #include "i40e.h" -#define I40E_MAX_MACVLAN_FILTERS 256 -#define I40E_MAX_VLAN_FILTERS 256 #define I40E_MAX_VLANID 4095 #define I40E_VIRTCHNL_SUPPORTED_QTYPES 2 -- cgit v0.10.2 From 1cdfd88f2de89cbfffb5813dc92b7e711920c731 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Mon, 28 Sep 2015 14:12:34 -0400 Subject: i40e: priv flag for controlling VEB stats Add an ethtool priv flag to enable and disable printing the VEB statistics. Change-ID: I7654054a3a73b08aa8310d94ee8fce6219107dd8 Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 7aaead3..1125464 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -103,6 +103,7 @@ #define I40E_PRIV_FLAGS_NPAR_FLAG BIT(0) #define I40E_PRIV_FLAGS_LINKPOLL_FLAG BIT(1) #define I40E_PRIV_FLAGS_FD_ATR BIT(2) +#define I40E_PRIV_FLAGS_VEB_STATS BIT(3) #define I40E_NVM_VERSION_LO_SHIFT 0 #define I40E_NVM_VERSION_LO_MASK (0xff << I40E_NVM_VERSION_LO_SHIFT) diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 5fe8ac0..cb88e7a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -229,6 +229,7 @@ static const char i40e_priv_flags_strings[][ETH_GSTRING_LEN] = { "NPAR", "LinkPolling", "flow-director-atr", + "veb-stats", }; #define I40E_PRIV_FLAGS_STR_LEN ARRAY_SIZE(i40e_priv_flags_strings) @@ -2670,6 +2671,8 @@ static u32 i40e_get_priv_flags(struct net_device *dev) I40E_PRIV_FLAGS_LINKPOLL_FLAG : 0; ret_flags |= pf->flags & I40E_FLAG_FD_ATR_ENABLED ? I40E_PRIV_FLAGS_FD_ATR : 0; + ret_flags |= pf->flags & I40E_FLAG_VEB_STATS_ENABLED ? + I40E_PRIV_FLAGS_VEB_STATS : 0; return ret_flags; } @@ -2701,6 +2704,11 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags) pf->auto_disable_flags |= I40E_FLAG_FD_ATR_ENABLED; } + if (flags & I40E_PRIV_FLAGS_VEB_STATS) + pf->flags |= I40E_FLAG_VEB_STATS_ENABLED; + else + pf->flags &= ~I40E_FLAG_VEB_STATS_ENABLED; + return 0; } -- cgit v0.10.2 From 947570e800e2ae3cbf827918a999aa88ee1600b0 Mon Sep 17 00:00:00 2001 From: Greg Bowers Date: Mon, 28 Sep 2015 14:12:35 -0400 Subject: i40e: Add support for non-willing Apps Adds support for setting a new bit in the Set Local LLDP MIB AQ command Type field. When set to 1, the bit indicates to FW that Apps should be treated as non-willing. When 0, FW behaves as before. Change-ID: I0d2101c1606c59c7188d3e6a0c7810e0f205233a Signed-off-by: Greg Bowers Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h index 02d5225..6584b6c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h @@ -2132,6 +2132,13 @@ I40E_CHECK_STRUCT_LEN(0x20, i40e_aqc_get_cee_dcb_cfg_resp); struct i40e_aqc_lldp_set_local_mib { #define SET_LOCAL_MIB_AC_TYPE_DCBX_SHIFT 0 #define SET_LOCAL_MIB_AC_TYPE_DCBX_MASK (1 << SET_LOCAL_MIB_AC_TYPE_DCBX_SHIFT) +#define SET_LOCAL_MIB_AC_TYPE_DCBX_MASK (1 << \ + SET_LOCAL_MIB_AC_TYPE_DCBX_SHIFT) +#define SET_LOCAL_MIB_AC_TYPE_LOCAL_MIB 0x0 +#define SET_LOCAL_MIB_AC_TYPE_NON_WILLING_APPS_SHIFT (1) +#define SET_LOCAL_MIB_AC_TYPE_NON_WILLING_APPS_MASK (1 << \ + SET_LOCAL_MIB_AC_TYPE_NON_WILLING_APPS_SHIFT) +#define SET_LOCAL_MIB_AC_TYPE_NON_WILLING_APPS 0x1 u8 type; u8 reserved0; __le16 length; diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index 6f69576..9c4a457 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -475,6 +475,8 @@ struct i40e_dcbx_config { u8 dcbx_mode; #define I40E_DCBX_MODE_CEE 0x1 #define I40E_DCBX_MODE_IEEE 0x2 + u8 app_mode; +#define I40E_DCBX_APPS_NON_WILLING 0x1 u32 numapps; u32 tlv_status; /* CEE mode TLV status */ struct i40e_dcb_ets_config etscfg; -- cgit v0.10.2 From ac26fc136c24edee53f1719e490d896fb07cd79b Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Mon, 28 Sep 2015 14:12:37 -0400 Subject: i40e/i40evf: moderate interrupts differently The XL710 hardware has a different interrupt moderation design that can support a limit of total interrupts per second per vector, in addition to the "number of interrupts per second" controls already established in the driver. This combination of hardware features allows us to set very low default latency settings but minimize the total CPU utilization by not making too many interrupts, should the user desire. The current driver implementation is still enabling the dynamic moderation in the driver, and only using the rx/tx-usecs limit in ethtool to limit the interrupt rate per second, by default. The new code implemented in this patch 2) adds init/use of the new "Interrupt Limit" register 3) adds ethtool knob to control/report the limits above Usage is ethtool -C ethx rx-usecs-high Where is number of microseconds to create a rate of 1/N interrupts per second, regardless of rx-usecs or tx-usecs values. Since there is a credit based scheme in the hardware, the rx-usecs and tx-usecs can be configured for very low latency for short bursts, but once the credit runs out the refill rate on the credits is limited by rx-usecs-high. Change-ID: I3a1075d3296123b0f4f50623c779b027af5b188d Signed-off-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 1125464..ad48f52 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -498,6 +498,7 @@ struct i40e_vsi { */ u16 rx_itr_setting; u16 tx_itr_setting; + u16 int_rate_limit; /* value in usecs */ u16 rss_table_size; u16 rss_size; diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index cb88e7a..1a50068 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -1845,6 +1845,14 @@ static int i40e_get_coalesce(struct net_device *netdev, ec->rx_coalesce_usecs = vsi->rx_itr_setting & ~I40E_ITR_DYNAMIC; ec->tx_coalesce_usecs = vsi->tx_itr_setting & ~I40E_ITR_DYNAMIC; + /* we use the _usecs_high to store/set the interrupt rate limit + * that the hardware supports, that almost but not quite + * fits the original intent of the ethtool variable, + * the rx_coalesce_usecs_high limits total interrupts + * per second from both tx/rx sources. + */ + ec->rx_coalesce_usecs_high = vsi->int_rate_limit; + ec->tx_coalesce_usecs_high = vsi->int_rate_limit; return 0; } @@ -1863,6 +1871,17 @@ static int i40e_set_coalesce(struct net_device *netdev, if (ec->tx_max_coalesced_frames_irq || ec->rx_max_coalesced_frames_irq) vsi->work_limit = ec->tx_max_coalesced_frames_irq; + /* tx_coalesce_usecs_high is ignored, use rx-usecs-high instead */ + if (ec->tx_coalesce_usecs_high != vsi->int_rate_limit) { + netif_info(pf, drv, netdev, "tx-usecs-high is not used, please program rx-usecs-high\n"); + return -EINVAL; + } + + if (ec->rx_coalesce_usecs_high >= INTRL_REG_TO_USEC(I40E_MAX_INTRL)) { + netif_info(pf, drv, netdev, "Invalid value, rx-usecs-high range is 0-235\n"); + return -EINVAL; + } + vector = vsi->base_vector; if ((ec->rx_coalesce_usecs >= (I40E_MIN_ITR << 1)) && (ec->rx_coalesce_usecs <= (I40E_MAX_ITR << 1))) { @@ -1876,6 +1895,8 @@ static int i40e_set_coalesce(struct net_device *netdev, return -EINVAL; } + vsi->int_rate_limit = ec->rx_coalesce_usecs_high; + if ((ec->tx_coalesce_usecs >= (I40E_MIN_ITR << 1)) && (ec->tx_coalesce_usecs <= (I40E_MAX_ITR << 1))) { vsi->tx_itr_setting = ec->tx_coalesce_usecs; @@ -1900,11 +1921,14 @@ static int i40e_set_coalesce(struct net_device *netdev, vsi->tx_itr_setting &= ~I40E_ITR_DYNAMIC; for (i = 0; i < vsi->num_q_vectors; i++, vector++) { + u16 intrl = INTRL_USEC_TO_REG(vsi->int_rate_limit); + q_vector = vsi->q_vectors[i]; q_vector->rx.itr = ITR_TO_REG(vsi->rx_itr_setting); wr32(hw, I40E_PFINT_ITRN(0, vector - 1), q_vector->rx.itr); q_vector->tx.itr = ITR_TO_REG(vsi->tx_itr_setting); wr32(hw, I40E_PFINT_ITRN(1, vector - 1), q_vector->tx.itr); + wr32(hw, I40E_PFINT_RATEN(vector - 1), intrl); i40e_flush(hw); } diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index d731095..722b0a1 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -2901,11 +2901,9 @@ static int i40e_vsi_configure(struct i40e_vsi *vsi) static void i40e_vsi_configure_msix(struct i40e_vsi *vsi) { struct i40e_pf *pf = vsi->back; - struct i40e_q_vector *q_vector; struct i40e_hw *hw = &pf->hw; u16 vector; int i, q; - u32 val; u32 qp; /* The interrupt indexing is offset by 1 in the PFINT_ITRn @@ -2915,7 +2913,8 @@ static void i40e_vsi_configure_msix(struct i40e_vsi *vsi) qp = vsi->base_queue; vector = vsi->base_vector; for (i = 0; i < vsi->num_q_vectors; i++, vector++) { - q_vector = vsi->q_vectors[i]; + struct i40e_q_vector *q_vector = vsi->q_vectors[i]; + q_vector->rx.itr = ITR_TO_REG(vsi->rx_itr_setting); q_vector->rx.latency_range = I40E_LOW_LATENCY; wr32(hw, I40E_PFINT_ITRN(I40E_RX_ITR, vector - 1), @@ -2924,10 +2923,14 @@ static void i40e_vsi_configure_msix(struct i40e_vsi *vsi) q_vector->tx.latency_range = I40E_LOW_LATENCY; wr32(hw, I40E_PFINT_ITRN(I40E_TX_ITR, vector - 1), q_vector->tx.itr); + wr32(hw, I40E_PFINT_RATEN(vector - 1), + INTRL_USEC_TO_REG(vsi->int_rate_limit)); /* Linked list for the queuepairs assigned to this vector */ wr32(hw, I40E_PFINT_LNKLSTN(vector - 1), qp); for (q = 0; q < q_vector->num_ringpairs; q++) { + u32 val; + val = I40E_QINT_RQCTL_CAUSE_ENA_MASK | (I40E_RX_ITR << I40E_QINT_RQCTL_ITR_INDX_SHIFT) | (vector << I40E_QINT_RQCTL_MSIX_INDX_SHIFT) | @@ -7040,6 +7043,7 @@ static int i40e_vsi_mem_alloc(struct i40e_pf *pf, enum i40e_vsi_type type) vsi->idx = vsi_idx; vsi->rx_itr_setting = pf->rx_itr_default; vsi->tx_itr_setting = pf->tx_itr_default; + vsi->int_rate_limit = 0; vsi->rss_table_size = (vsi->type == I40E_VSI_MAIN) ? pf->rss_table_size : 64; vsi->netdev_registered = false; diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index 75cecfa..7c0ed84 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -35,6 +35,7 @@ #define I40E_ITR_20K 0x0019 #define I40E_ITR_8K 0x003E #define I40E_ITR_4K 0x007A +#define I40E_MAX_INTRL 0x3B /* reg uses 4 usec resolution */ #define I40E_ITR_RX_DEF I40E_ITR_8K #define I40E_ITR_TX_DEF I40E_ITR_4K #define I40E_ITR_DYNAMIC 0x8000 /* use top bit as a flag */ @@ -44,6 +45,15 @@ #define ITR_TO_REG(setting) ((setting & ~I40E_ITR_DYNAMIC) >> 1) #define ITR_IS_DYNAMIC(setting) (!!(setting & I40E_ITR_DYNAMIC)) #define ITR_REG_TO_USEC(itr_reg) (itr_reg << 1) +/* 0x40 is the enable bit for interrupt rate limiting, and must be set if + * the value of the rate limit is non-zero + */ +#define INTRL_ENA BIT(6) +#define INTRL_REG_TO_USEC(intrl) ((intrl & ~INTRL_ENA) << 2) +#define INTRL_USEC_TO_REG(set) ((set) ? ((set) >> 2) | INTRL_ENA : 0) +#define I40E_INTRL_8K 125 /* 8000 ints/sec */ +#define I40E_INTRL_62K 16 /* 62500 ints/sec */ +#define I40E_INTRL_83K 12 /* 83333 ints/sec */ #define I40E_QUEUE_END_OF_LIST 0x7FF diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index 0c13ece..c4f5a4e 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -35,6 +35,7 @@ #define I40E_ITR_20K 0x0019 #define I40E_ITR_8K 0x003E #define I40E_ITR_4K 0x007A +#define I40E_MAX_INTRL 0x3B /* reg uses 4 usec resolution */ #define I40E_ITR_RX_DEF I40E_ITR_8K #define I40E_ITR_TX_DEF I40E_ITR_4K #define I40E_ITR_DYNAMIC 0x8000 /* use top bit as a flag */ @@ -44,6 +45,15 @@ #define ITR_TO_REG(setting) ((setting & ~I40E_ITR_DYNAMIC) >> 1) #define ITR_IS_DYNAMIC(setting) (!!(setting & I40E_ITR_DYNAMIC)) #define ITR_REG_TO_USEC(itr_reg) (itr_reg << 1) +/* 0x40 is the enable bit for interrupt rate limiting, and must be set if + * the value of the rate limit is non-zero + */ +#define INTRL_ENA BIT(6) +#define INTRL_REG_TO_USEC(intrl) ((intrl & ~INTRL_ENA) << 2) +#define INTRL_USEC_TO_REG(set) ((set) ? ((set) >> 2) | INTRL_ENA : 0) +#define I40E_INTRL_8K 125 /* 8000 ints/sec */ +#define I40E_INTRL_62K 16 /* 62500 ints/sec */ +#define I40E_INTRL_83K 12 /* 83333 ints/sec */ #define I40E_QUEUE_END_OF_LIST 0x7FF -- cgit v0.10.2 From 5d38c93e7111a68fc7b9f771bf420d9e026a21de Mon Sep 17 00:00:00 2001 From: Jingjing Wu Date: Mon, 28 Sep 2015 14:12:39 -0400 Subject: i40e: reset the invalid msg counter in vf when a valid msg is received When the number of invalid messages from a VF is exceeded, the VF will be disabled, due to the invalid messages. This happens if other VF drivers (like DPDK) send a message through the driver's mailbox (aka virtchannel) interface, but the message is not supported by the i40e pf driver, such as CONFIG_PROMISCUOUS_MODE. This patch changes the num_invalid_msgs in struct i40e_vf to record the continuous invalid msgs, and it will be reset when a valid msg is received. Change-ID: Iaec42fd3dcdd281476b3518be23261dd46fc3718 Signed-off-by: Jingjing Wu Signed-off-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index fd99cd2..a35a204 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -1102,6 +1102,8 @@ static int i40e_vc_send_msg_to_vf(struct i40e_vf *vf, u32 v_opcode, } } else { vf->num_valid_msgs++; + /* reset the invalid counter, if a valid message is received. */ + vf->num_invalid_msgs = 0; } aq_ret = i40e_aq_send_msg_to_vf(hw, abs_vf_id, v_opcode, v_retval, diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h index 7fe94e8..da44995 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h @@ -96,7 +96,8 @@ struct i40e_vf { u8 num_queue_pairs; /* num of qps assigned to VF vsis */ u64 num_mdd_events; /* num of mdd events detected */ - u64 num_invalid_msgs; /* num of malformed or invalid msgs detected */ + /* num of continuous malformed or invalid msgs detected */ + u64 num_invalid_msgs; u64 num_valid_msgs; /* num of valid msgs detected */ unsigned long vf_caps; /* vf's adv. capabilities */ -- cgit v0.10.2 From cc7e406cb967ccc84cd2141398bba24b07d68788 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Mon, 28 Sep 2015 14:12:40 -0400 Subject: i40evf: correctly populate vlan_features The vlan_features field was correctly being set to the same value as the netdev features field. However, this was being done before the features were actually being set up, leaving the vlan_features empty. Also, after a reset, vlan_features will be incorrectly assigned the previous netdev feature flags, which can contain VLAN feature bits. This makes the VLAN code angry and will cause a stack dump. To fix these issues, set up the netdev features first, then mask out the VLAN feature bits when assigning vlan_features. Change-ID: Ib0548869dc83cf6a841cb8697dd94c12359ba4d2 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 139801f..46af35f 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -2123,7 +2123,10 @@ int i40evf_process_config(struct i40evf_adapter *adapter) if (adapter->vf_res->vf_offload_flags & I40E_VIRTCHNL_VF_OFFLOAD_VLAN) { - netdev->vlan_features = netdev->features; + netdev->vlan_features = netdev->features & + ~(NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_HW_VLAN_CTAG_RX | + NETIF_F_HW_VLAN_CTAG_FILTER); netdev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER; -- cgit v0.10.2 From 48becae60f7e461b4b204e6608dc6beebe6c8aff Mon Sep 17 00:00:00 2001 From: Catherine Sullivan Date: Mon, 28 Sep 2015 14:12:41 -0400 Subject: i40e: Recognize 1000Base_T_Optical phy type when link is up 1000Base_T_Optical got added to the function that figures out what is supported when link is down but not when link is up. Add it in there too so that we display the correct information. Change-ID: I85ebcdfa7c02d898c44c673b1500552a53c8042e Signed-off-by: Catherine Sullivan Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 1a50068..767b1db 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -307,6 +307,12 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw, if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) ecmd->advertising |= ADVERTISED_1000baseT_Full; break; + case I40E_PHY_TYPE_1000BASE_T_OPTICAL: + ecmd->supported = SUPPORTED_Autoneg | + SUPPORTED_1000baseT_Full; + ecmd->advertising = ADVERTISED_Autoneg | + ADVERTISED_1000baseT_Full; + break; case I40E_PHY_TYPE_100BASE_TX: ecmd->supported = SUPPORTED_Autoneg | SUPPORTED_100baseT_Full; -- cgit v0.10.2 From 3f7e5c330e19da2ac822ea1d02b744a24497e61b Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Mon, 28 Sep 2015 14:12:42 -0400 Subject: i40evf: relax and stagger init timing a bit On some devices, in some systems, in some configurations, the VFs would fail to initialize the first time you loaded the driver. To correct this, increase the delay time for the init task slightly, and wait longer before giving up. If we enable VFs and load the VF driver in the same kernel as the PF driver, we can totally overwhelm the PF driver with AQ requests because all of the instances try to initialize at the same time. To help alleviate this, stagger the initial scheduling of the init task using the PCIe function as a multiplier. We mask off the function to only three bits so no instance has to wait too long. With these two changes, initializing 128 VFs on a single device goes from four minutes to just a few seconds. Change-ID: If3d8720c1c4e838ab36d8781d9ec295a62380936 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index c74f614..22841c6 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -87,7 +87,7 @@ struct i40e_vsi { #define I40EVF_MAX_RXBUFFER 16384 /* largest size for single descriptor */ #define I40EVF_MAX_AQ_BUF_SIZE 4096 #define I40EVF_AQ_LEN 32 -#define I40EVF_AQ_MAX_ERR 10 /* times to try before resetting AQ */ +#define I40EVF_AQ_MAX_ERR 20 /* times to try before resetting AQ */ #define MAXIMUM_ETHERNET_VLAN_SIZE (VLAN_ETH_FRAME_LEN + ETH_FCS_LEN) diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 46af35f..4f3d571 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -2344,7 +2344,7 @@ static void i40evf_init_task(struct work_struct *work) } return; restart: - schedule_delayed_work(&adapter->init_task, msecs_to_jiffies(20)); + schedule_delayed_work(&adapter->init_task, msecs_to_jiffies(30)); return; err_register: @@ -2361,7 +2361,7 @@ err: adapter->flags |= I40EVF_FLAG_PF_COMMS_FAILED; return; /* do not reschedule */ } - schedule_delayed_work(&adapter->init_task, HZ / 2); + schedule_delayed_work(&adapter->init_task, HZ); } /** -- cgit v0.10.2 From 628f096d8d244f54c3595a478b6e672cdb6c04ed Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Mon, 28 Sep 2015 14:12:43 -0400 Subject: i40e: increase AQ work limit With 64 VFs, we can easily overwhelm the AQ on the PF if we have too low a limit on the number of AQ requests. This leads to ARQ overflow errors, and occasionally VFs that fail to initialize. Since we really only hit this condition on initial VF driver load, the requests that we process are lightweight, so this extra work doesn't cause problems for the PF driver. Change-ID: I620221520d8af987df6ace9ba938ffaf22107681 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index ad48f52..7a58b1f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -93,7 +93,7 @@ #endif /* I40E_FCOE */ #define I40E_MAX_AQ_BUF_SIZE 4096 #define I40E_AQ_LEN 256 -#define I40E_AQ_WORK_LIMIT 32 +#define I40E_AQ_WORK_LIMIT 66 /* max number of VFs + a little */ #define I40E_MAX_USER_PRIORITY 8 #define I40E_DEFAULT_MSG_ENABLE 4 #define I40E_QUEUE_WAIT_RETRY_LIMIT 10 -- cgit v0.10.2 From d1d39516e41d7ad107e807ebeee1519c412bb0e3 Mon Sep 17 00:00:00 2001 From: Catherine Sullivan Date: Mon, 28 Sep 2015 14:12:44 -0400 Subject: i40e/i40evf: Bump i40e to 1.3.34 and i40evf to 1.3.21 Bump. Change-ID: I7ec818a507554648675b9b245ced9e6b6bd9ed4e Signed-off-by: Catherine Sullivan Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 722b0a1..87a5d09 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -39,7 +39,7 @@ static const char i40e_driver_string[] = #define DRV_VERSION_MAJOR 1 #define DRV_VERSION_MINOR 3 -#define DRV_VERSION_BUILD 28 +#define DRV_VERSION_BUILD 34 #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 4f3d571..4c4340c 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -34,7 +34,7 @@ char i40evf_driver_name[] = "i40evf"; static const char i40evf_driver_string[] = "Intel(R) XL710/X710 Virtual Function Network Driver"; -#define DRV_VERSION "1.3.19" +#define DRV_VERSION "1.3.21" const char i40evf_driver_version[] = DRV_VERSION; static const char i40evf_copyright[] = "Copyright (c) 2013 - 2015 Intel Corporation."; -- cgit v0.10.2 From 573c7ba006edbecff0714db651dd3602b9d0a6a0 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 16 Oct 2015 14:01:22 +0200 Subject: net: introduce pre-change upper device notifier This newly introduced netdevice notifier is called before actual change upper happens. That provides a possibility for notifier handlers to know upper change will happen and react to it, including possibility to forbid the change. That is valuable for drivers which can check if the upper device linkage is supported and forbid that in case it is not. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index b337440..69fdd42 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2106,6 +2106,7 @@ struct pcpu_sw_netstats { #define NETDEV_PRECHANGEMTU 0x0017 /* notify before mtu change happened */ #define NETDEV_CHANGEINFODATA 0x0018 #define NETDEV_BONDING_INFO 0x0019 +#define NETDEV_PRECHANGEUPPER 0x001A int register_netdevice_notifier(struct notifier_block *nb); int unregister_netdevice_notifier(struct notifier_block *nb); diff --git a/net/core/dev.c b/net/core/dev.c index a229bf0..1225b4b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5346,6 +5346,12 @@ static int __netdev_upper_dev_link(struct net_device *dev, changeupper_info.master = master; changeupper_info.linking = true; + ret = call_netdevice_notifiers_info(NETDEV_PRECHANGEUPPER, dev, + &changeupper_info.info); + ret = notifier_to_errno(ret); + if (ret) + return ret; + ret = __netdev_adjacent_dev_link_neighbour(dev, upper_dev, private, master); if (ret) @@ -5488,6 +5494,9 @@ void netdev_upper_dev_unlink(struct net_device *dev, changeupper_info.master = netdev_master_upper_dev_get(dev) == upper_dev; changeupper_info.linking = false; + call_netdevice_notifiers_info(NETDEV_PRECHANGEUPPER, dev, + &changeupper_info.info); + __netdev_adjacent_dev_unlink_neighbour(dev, upper_dev); /* Here is the tricky part. We must remove all dev's lower -- cgit v0.10.2 From bc2055f878acb1fbbb3f3e7cb851f2e318def010 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 16 Oct 2015 14:01:23 +0200 Subject: mlxsw: Enable configuration of flooding domains As part of the introduction of L2 offloads, allow different ports to join/leave the flooding domain, according to user configuration. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 7b245af..4099d4b3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -363,7 +363,7 @@ static inline void mlxsw_reg_sftr_pack(char *payload, unsigned int flood_table, unsigned int index, enum mlxsw_flood_table_type table_type, - unsigned int range) + unsigned int range, u8 port, bool set) { MLXSW_REG_ZERO(sftr, payload); mlxsw_reg_sftr_swid_set(payload, 0); @@ -371,8 +371,8 @@ static inline void mlxsw_reg_sftr_pack(char *payload, mlxsw_reg_sftr_index_set(payload, index); mlxsw_reg_sftr_table_type_set(payload, table_type); mlxsw_reg_sftr_range_set(payload, range); - mlxsw_reg_sftr_port_set(payload, MLXSW_PORT_CPU_PORT, 1); - mlxsw_reg_sftr_port_mask_set(payload, MLXSW_PORT_CPU_PORT, 1); + mlxsw_reg_sftr_port_set(payload, port, set); + mlxsw_reg_sftr_port_mask_set(payload, port, 1); } /* SPMLR - Switch Port MAC Learning Register diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 4f72e0a..2fd2279 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -1377,7 +1377,8 @@ static int mlxsw_sx_flood_init(struct mlxsw_sx *mlxsw_sx) sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL); if (!sftr_pl) return -ENOMEM; - mlxsw_reg_sftr_pack(sftr_pl, 0, 0, MLXSW_REG_SFGC_TABLE_TYPE_SINGLE, 0); + mlxsw_reg_sftr_pack(sftr_pl, 0, 0, MLXSW_REG_SFGC_TABLE_TYPE_SINGLE, 0, + MLXSW_PORT_CPU_PORT, true); err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sftr), sftr_pl); kfree(sftr_pl); if (err) -- cgit v0.10.2 From 453b6a8dd8338d692770bda89a52bc71c8fea2b8 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 16 Oct 2015 14:01:24 +0200 Subject: mlxsw: cmd: Introduce per-FID flooding tables In the newly introduced Spectrum switch ASIC, packets destined to not offloaded netdevs will be classified to special FIDs (vFIDs) in the device and flooded to the CPU port. The flooding table used is of type per-FID, which allows one to create different flooding domains for different vFIDs. While using a simple single-entry flood table is certainly sufficient at this point, we do plan to offload 802.1D bridges involving VLAN interfaces, thus making this change necessary. Add support for this flooding table type, by exposing the configuration of the number of tables from this type and their size. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/cmd.h b/drivers/net/ethernet/mellanox/mlxsw/cmd.h index c7889f4..010eb5d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/cmd.h +++ b/drivers/net/ethernet/mellanox/mlxsw/cmd.h @@ -674,6 +674,19 @@ MLXSW_ITEM32(cmd_mbox, config_profile, max_vid_flood_tables, 0x30, 8, 4); */ MLXSW_ITEM32(cmd_mbox, config_profile, flood_mode, 0x30, 0, 2); +/* cmd_mbox_config_profile_max_fid_flood_tables + * Maximum number of per-FID flooding tables. + * + * Note: This flooding tables cover special FIDs only (vFIDs), starting at + * FID value 4K and higher. + */ +MLXSW_ITEM32(cmd_mbox, config_profile, max_fid_flood_tables, 0x38, 24, 4); + +/* cmd_mbox_config_profile_fid_flood_table_size + * The size (number of entries) of each per-FID table. + */ +MLXSW_ITEM32(cmd_mbox, config_profile, fid_flood_table_size, 0x38, 0, 16); + /* cmd_mbox_config_profile_max_ib_mc * Maximum number of multicast FDB records for InfiniBand * FDB (in 512 chunks) per InfiniBand switch partition. diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index 1658084..8bb85ea 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -153,6 +153,8 @@ struct mlxsw_config_profile { u8 max_flood_tables; u8 max_vid_flood_tables; u8 flood_mode; + u8 max_fid_flood_tables; + u16 fid_flood_table_size; u16 max_ib_mc; u16 max_pkey; u8 ar_sec; diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c index 974ce47..faa4f3f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c @@ -1214,6 +1214,10 @@ static int mlxsw_pci_config_profile(struct mlxsw_pci *mlxsw_pci, char *mbox, mbox, profile->max_flood_tables); mlxsw_cmd_mbox_config_profile_max_vid_flood_tables_set( mbox, profile->max_vid_flood_tables); + mlxsw_cmd_mbox_config_profile_max_fid_flood_tables_set( + mbox, profile->max_fid_flood_tables); + mlxsw_cmd_mbox_config_profile_fid_flood_table_size_set( + mbox, profile->fid_flood_table_size); } if (profile->used_flood_mode) { mlxsw_cmd_mbox_config_profile_set_flood_mode_set( -- cgit v0.10.2 From 12fd35ab8af9fe32fce6b36881461d6f62408f70 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 16 Oct 2015 14:01:25 +0200 Subject: mlxsw: cmd: Introduce FID-offset flooding tables Packets destined to offloaded netdevs will be classified to FIDs in the device and flooded in case of BUM. The flooding table used is of type FID-offset, which allows one to create different flooding domains for different FIDs and specify the offset in the flooding table for each FID (not necessarily equal to FID or VID). Add support for this flooding table type, by exposing the configuration of the number of tables from this type and their size. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/cmd.h b/drivers/net/ethernet/mellanox/mlxsw/cmd.h index 010eb5d..cd63b82 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/cmd.h +++ b/drivers/net/ethernet/mellanox/mlxsw/cmd.h @@ -674,6 +674,18 @@ MLXSW_ITEM32(cmd_mbox, config_profile, max_vid_flood_tables, 0x30, 8, 4); */ MLXSW_ITEM32(cmd_mbox, config_profile, flood_mode, 0x30, 0, 2); +/* cmd_mbox_config_profile_max_fid_offset_flood_tables + * Maximum number of FID-offset flooding tables. + */ +MLXSW_ITEM32(cmd_mbox, config_profile, + max_fid_offset_flood_tables, 0x34, 24, 4); + +/* cmd_mbox_config_profile_fid_offset_flood_table_size + * The size (number of entries) of each FID-offset flood table. + */ +MLXSW_ITEM32(cmd_mbox, config_profile, + fid_offset_flood_table_size, 0x34, 0, 16); + /* cmd_mbox_config_profile_max_fid_flood_tables * Maximum number of per-FID flooding tables. * diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index 8bb85ea..e92ab27 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -153,6 +153,8 @@ struct mlxsw_config_profile { u8 max_flood_tables; u8 max_vid_flood_tables; u8 flood_mode; + u8 max_fid_offset_flood_tables; + u16 fid_offset_flood_table_size; u8 max_fid_flood_tables; u16 fid_flood_table_size; u16 max_ib_mc; diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c index faa4f3f..0fa44c8 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c @@ -1214,6 +1214,10 @@ static int mlxsw_pci_config_profile(struct mlxsw_pci *mlxsw_pci, char *mbox, mbox, profile->max_flood_tables); mlxsw_cmd_mbox_config_profile_max_vid_flood_tables_set( mbox, profile->max_vid_flood_tables); + mlxsw_cmd_mbox_config_profile_max_fid_offset_flood_tables_set( + mbox, profile->max_fid_offset_flood_tables); + mlxsw_cmd_mbox_config_profile_fid_offset_flood_table_size_set( + mbox, profile->fid_offset_flood_table_size); mlxsw_cmd_mbox_config_profile_max_fid_flood_tables_set( mbox, profile->max_fid_flood_tables); mlxsw_cmd_mbox_config_profile_fid_flood_table_size_set( -- cgit v0.10.2 From 7b0989b5bcac950c05d72dee5f6dcad38d355b3f Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 16 Oct 2015 14:01:26 +0200 Subject: mlxsw: item: Make src arg of memcpy_to helper const Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/item.h b/drivers/net/ethernet/mellanox/mlxsw/item.h index ffd55d0..5a5e737 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/item.h +++ b/drivers/net/ethernet/mellanox/mlxsw/item.h @@ -176,7 +176,7 @@ static inline void __mlxsw_item_memcpy_from(char *buf, char *dst, memcpy(dst, &buf[item->offset], item->size.bytes); } -static inline void __mlxsw_item_memcpy_to(char *buf, char *src, +static inline void __mlxsw_item_memcpy_to(char *buf, const char *src, struct mlxsw_item *item) { memcpy(&buf[item->offset], src, item->size.bytes); @@ -374,7 +374,7 @@ mlxsw_##_type##_##_cname##_##_iname##_memcpy_from(char *buf, char *dst) \ __mlxsw_item_memcpy_from(buf, dst, &__ITEM_NAME(_type, _cname, _iname));\ } \ static inline void \ -mlxsw_##_type##_##_cname##_##_iname##_memcpy_to(char *buf, char *src) \ +mlxsw_##_type##_##_cname##_##_iname##_memcpy_to(char *buf, const char *src) \ { \ __mlxsw_item_memcpy_to(buf, src, &__ITEM_NAME(_type, _cname, _iname)); \ } -- cgit v0.10.2 From d64b1592535e390cdc0605a5b8d4b6665be47003 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 16 Oct 2015 14:01:27 +0200 Subject: mlxsw: item: Add MLXSW_ITEM_BUF_INDEXED helper Add missing item helper which allows to access char bufs on multiple offsets. This is needed by SFD and SFN register definitions. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/item.h b/drivers/net/ethernet/mellanox/mlxsw/item.h index 5a5e737..1c5e43e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/item.h +++ b/drivers/net/ethernet/mellanox/mlxsw/item.h @@ -171,15 +171,21 @@ 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) + struct mlxsw_item *item, + unsigned short index) { - memcpy(dst, &buf[item->offset], item->size.bytes); + unsigned int offset = __mlxsw_item_offset(item, index, sizeof(char)); + + memcpy(dst, &buf[offset], item->size.bytes); } static inline void __mlxsw_item_memcpy_to(char *buf, const char *src, - struct mlxsw_item *item) + struct mlxsw_item *item, + unsigned short index) { - memcpy(&buf[item->offset], src, item->size.bytes); + unsigned int offset = __mlxsw_item_offset(item, index, sizeof(char)); + + memcpy(&buf[offset], src, item->size.bytes); } static inline u16 @@ -371,12 +377,40 @@ static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = { \ static inline void \ mlxsw_##_type##_##_cname##_##_iname##_memcpy_from(char *buf, char *dst) \ { \ - __mlxsw_item_memcpy_from(buf, dst, &__ITEM_NAME(_type, _cname, _iname));\ + __mlxsw_item_memcpy_from(buf, dst, \ + &__ITEM_NAME(_type, _cname, _iname), 0); \ } \ static inline void \ mlxsw_##_type##_##_cname##_##_iname##_memcpy_to(char *buf, const char *src) \ { \ - __mlxsw_item_memcpy_to(buf, src, &__ITEM_NAME(_type, _cname, _iname)); \ + __mlxsw_item_memcpy_to(buf, src, \ + &__ITEM_NAME(_type, _cname, _iname), 0); \ +} + +#define MLXSW_ITEM_BUF_INDEXED(_type, _cname, _iname, _offset, _sizebytes, \ + _step, _instepoffset) \ +static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = { \ + .offset = _offset, \ + .step = _step, \ + .in_step_offset = _instepoffset, \ + .size = {.bytes = _sizebytes,}, \ + .name = #_type "_" #_cname "_" #_iname, \ +}; \ +static inline void \ +mlxsw_##_type##_##_cname##_##_iname##_memcpy_from(char *buf, \ + unsigned short index, \ + char *dst) \ +{ \ + __mlxsw_item_memcpy_from(buf, dst, \ + &__ITEM_NAME(_type, _cname, _iname), index); \ +} \ +static inline void \ +mlxsw_##_type##_##_cname##_##_iname##_memcpy_to(char *buf, \ + unsigned short index, \ + const char *src) \ +{ \ + __mlxsw_item_memcpy_to(buf, src, \ + &__ITEM_NAME(_type, _cname, _iname), index); \ } #define MLXSW_ITEM_BIT_ARRAY(_type, _cname, _iname, _offset, _sizebytes, \ -- cgit v0.10.2 From 236033b33c09ea9e34ce320d5cf9ef527077d7d5 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 16 Oct 2015 14:01:28 +0200 Subject: mlxsw: reg: Add Switch Filtering Database register definition Add the SFD register which is responsible for filtering database manipulation, including static and dynamic FDB entries. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 4099d4b3..c0cc5f1 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -157,6 +157,220 @@ static inline void mlxsw_reg_sspr_pack(char *payload, u8 local_port) mlxsw_reg_sspr_system_port_set(payload, local_port); } +/* SFD - Switch Filtering Database + * ------------------------------- + * The following register defines the access to the filtering database. + * The register supports querying, adding, removing and modifying the database. + * The access is optimized for bulk updates in which case more than one + * FDB record is present in the same command. + */ +#define MLXSW_REG_SFD_ID 0x200A +#define MLXSW_REG_SFD_BASE_LEN 0x10 /* base length, without records */ +#define MLXSW_REG_SFD_REC_LEN 0x10 /* record length */ +#define MLXSW_REG_SFD_REC_MAX_COUNT 64 +#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, +}; + +/* reg_sfd_swid + * Switch partition ID for queries. Reserved on Write. + * Access: Index + */ +MLXSW_ITEM32(reg, sfd, swid, 0x00, 24, 8); + +enum mlxsw_reg_sfd_op { + /* Dump entire FDB a (process according to record_locator) */ + MLXSW_REG_SFD_OP_QUERY_DUMP = 0, + /* Query records by {MAC, VID/FID} value */ + MLXSW_REG_SFD_OP_QUERY_QUERY = 1, + /* Query and clear activity. Query records by {MAC, VID/FID} value */ + MLXSW_REG_SFD_OP_QUERY_QUERY_AND_CLEAR_ACTIVITY = 2, + /* Test. Response indicates if each of the records could be + * added to the FDB. + */ + MLXSW_REG_SFD_OP_WRITE_TEST = 0, + /* Add/modify. Aged-out records cannot be added. This command removes + * the learning notification of the {MAC, VID/FID}. Response includes + * the entries that were added to the FDB. + */ + MLXSW_REG_SFD_OP_WRITE_EDIT = 1, + /* Remove record by {MAC, VID/FID}. This command also removes + * the learning notification and aged-out notifications + * of the {MAC, VID/FID}. The response provides current (pre-removal) + * entries as non-aged-out. + */ + MLXSW_REG_SFD_OP_WRITE_REMOVE = 2, + /* Remove learned notification by {MAC, VID/FID}. The response provides + * the removed learning notification. + */ + MLXSW_REG_SFD_OP_WRITE_REMOVE_NOTIFICATION = 2, +}; + +/* reg_sfd_op + * Operation. + * Access: OP + */ +MLXSW_ITEM32(reg, sfd, op, 0x04, 30, 2); + +/* reg_sfd_record_locator + * Used for querying the FDB. Use record_locator=0 to initiate the + * query. When a record is returned, a new record_locator is + * returned to be used in the subsequent query. + * Reserved for database update. + * Access: Index + */ +MLXSW_ITEM32(reg, sfd, record_locator, 0x04, 0, 30); + +/* reg_sfd_num_rec + * Request: Number of records to read/add/modify/remove + * Response: Number of records read/added/replaced/removed + * See above description for more details. + * Ranges 0..64 + * Access: RW + */ +MLXSW_ITEM32(reg, sfd, num_rec, 0x08, 0, 8); + +static inline void mlxsw_reg_sfd_pack(char *payload, enum mlxsw_reg_sfd_op op, + u32 record_locator) +{ + MLXSW_REG_ZERO(sfd, payload); + mlxsw_reg_sfd_op_set(payload, op); + mlxsw_reg_sfd_record_locator_set(payload, record_locator); +} + +/* reg_sfd_rec_swid + * Switch partition ID. + * Access: Index + */ +MLXSW_ITEM32_INDEXED(reg, sfd, rec_swid, MLXSW_REG_SFD_BASE_LEN, 24, 8, + MLXSW_REG_SFD_REC_LEN, 0x00, false); + +enum mlxsw_reg_sfd_rec_type { + MLXSW_REG_SFD_REC_TYPE_UNICAST = 0x0, +}; + +/* reg_sfd_rec_type + * FDB record type. + * Access: RW + */ +MLXSW_ITEM32_INDEXED(reg, sfd, rec_type, MLXSW_REG_SFD_BASE_LEN, 20, 4, + MLXSW_REG_SFD_REC_LEN, 0x00, false); + +enum mlxsw_reg_sfd_rec_policy { + /* Replacement disabled, aging disabled. */ + MLXSW_REG_SFD_REC_POLICY_STATIC_ENTRY = 0, + /* (mlag remote): Replacement enabled, aging disabled, + * learning notification enabled on this port. + */ + MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_MLAG = 1, + /* (ingress device): Replacement enabled, aging enabled. */ + MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_INGRESS = 3, +}; + +/* reg_sfd_rec_policy + * Policy. + * Access: RW + */ +MLXSW_ITEM32_INDEXED(reg, sfd, rec_policy, MLXSW_REG_SFD_BASE_LEN, 18, 2, + MLXSW_REG_SFD_REC_LEN, 0x00, false); + +/* reg_sfd_rec_a + * Activity. Set for new static entries. Set for static entries if a frame SMAC + * lookup hits on the entry. + * To clear the a bit, use "query and clear activity" op. + * Access: RO + */ +MLXSW_ITEM32_INDEXED(reg, sfd, rec_a, MLXSW_REG_SFD_BASE_LEN, 16, 1, + MLXSW_REG_SFD_REC_LEN, 0x00, false); + +/* reg_sfd_rec_mac + * MAC address. + * Access: Index + */ +MLXSW_ITEM_BUF_INDEXED(reg, sfd, rec_mac, MLXSW_REG_SFD_BASE_LEN, 6, + MLXSW_REG_SFD_REC_LEN, 0x02); + +enum mlxsw_reg_sfd_rec_action { + /* forward */ + MLXSW_REG_SFD_REC_ACTION_NOP = 0, + /* forward and trap, trap_id is FDB_TRAP */ + MLXSW_REG_SFD_REC_ACTION_MIRROR_TO_CPU = 1, + /* trap and do not forward, trap_id is FDB_TRAP */ + MLXSW_REG_SFD_REC_ACTION_TRAP = 3, + MLXSW_REG_SFD_REC_ACTION_DISCARD_ERROR = 15, +}; + +/* reg_sfd_rec_action + * Action to apply on the packet. + * Note: Dynamic entries can only be configured with NOP action. + * Access: RW + */ +MLXSW_ITEM32_INDEXED(reg, sfd, rec_action, MLXSW_REG_SFD_BASE_LEN, 28, 4, + MLXSW_REG_SFD_REC_LEN, 0x0C, false); + +/* reg_sfd_uc_sub_port + * LAG sub port. + * Must be 0 if multichannel VEPA is not enabled. + * Access: RW + */ +MLXSW_ITEM32_INDEXED(reg, sfd, uc_sub_port, MLXSW_REG_SFD_BASE_LEN, 16, 8, + MLXSW_REG_SFD_REC_LEN, 0x08, false); + +/* reg_sfd_uc_fid_vid + * Filtering ID or VLAN ID + * For SwitchX and SwitchX-2: + * - Dynamic entries (policy 2,3) use FID + * - Static entries (policy 0) use VID + * - When independent learning is configured, VID=FID + * For Spectrum: use FID for both Dynamic and Static entries. + * VID should not be used. + * Access: Index + */ +MLXSW_ITEM32_INDEXED(reg, sfd, uc_fid_vid, MLXSW_REG_SFD_BASE_LEN, 0, 16, + MLXSW_REG_SFD_REC_LEN, 0x08, false); + +/* reg_sfd_uc_system_port + * Unique port identifier for the final destination of the packet. + * Access: RW + */ +MLXSW_ITEM32_INDEXED(reg, sfd, uc_system_port, MLXSW_REG_SFD_BASE_LEN, 0, 16, + MLXSW_REG_SFD_REC_LEN, 0x0C, false); + +static inline void mlxsw_reg_sfd_uc_pack(char *payload, int rec_index, + enum mlxsw_reg_sfd_rec_policy policy, + const char *mac, u16 vid, + enum mlxsw_reg_sfd_rec_action action, + u8 local_port) +{ + u8 num_rec = mlxsw_reg_sfd_num_rec_get(payload); + + if (rec_index >= num_rec) + mlxsw_reg_sfd_num_rec_set(payload, rec_index + 1); + mlxsw_reg_sfd_rec_swid_set(payload, rec_index, 0); + mlxsw_reg_sfd_rec_type_set(payload, rec_index, + MLXSW_REG_SFD_REC_TYPE_UNICAST); + mlxsw_reg_sfd_rec_policy_set(payload, rec_index, policy); + mlxsw_reg_sfd_rec_mac_memcpy_to(payload, rec_index, mac); + mlxsw_reg_sfd_uc_sub_port_set(payload, rec_index, 0); + mlxsw_reg_sfd_uc_fid_vid_set(payload, rec_index, vid); + mlxsw_reg_sfd_rec_action_set(payload, rec_index, action); + mlxsw_reg_sfd_uc_system_port_set(payload, rec_index, local_port); +} + +static inline void +mlxsw_reg_sfd_uc_unpack(char *payload, int rec_index, + char *mac, u16 *p_vid, + u8 *p_local_port) +{ + mlxsw_reg_sfd_rec_mac_memcpy_from(payload, rec_index, mac); + *p_vid = mlxsw_reg_sfd_uc_fid_vid_get(payload, rec_index); + *p_local_port = mlxsw_reg_sfd_uc_system_port_get(payload, rec_index); +} + /* SPMS - Switch Port MSTP/RSTP State Register * ------------------------------------------- * Configures the spanning tree state of a physical port. @@ -1251,6 +1465,8 @@ static inline const char *mlxsw_reg_id_str(u16 reg_id) return "SPAD"; case MLXSW_REG_SSPR_ID: return "SSPR"; + case MLXSW_REG_SFD_ID: + return "SFD"; case MLXSW_REG_SPMS_ID: return "SPMS"; case MLXSW_REG_SFGC_ID: -- cgit v0.10.2 From f5d88f589297a9b5fb110bf01d621d8c09fb7512 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 16 Oct 2015 14:01:29 +0200 Subject: mlxsw: reg: Add Switch FDB Notification register definition Add SFN register which is used to poll for newly added and aged-out FDB entries. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index c0cc5f1..8afa2b6 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -371,6 +371,105 @@ mlxsw_reg_sfd_uc_unpack(char *payload, int rec_index, *p_local_port = mlxsw_reg_sfd_uc_system_port_get(payload, rec_index); } +/* SFN - Switch FDB Notification Register + * ------------------------------------------- + * The switch provides notifications on newly learned FDB entries and + * aged out entries. The notifications can be polled by software. + */ +#define MLXSW_REG_SFN_ID 0x200B +#define MLXSW_REG_SFN_BASE_LEN 0x10 /* base length, without records */ +#define MLXSW_REG_SFN_REC_LEN 0x10 /* record length */ +#define MLXSW_REG_SFN_REC_MAX_COUNT 64 +#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, +}; + +/* reg_sfn_swid + * Switch partition ID. + * Access: Index + */ +MLXSW_ITEM32(reg, sfn, swid, 0x00, 24, 8); + +/* reg_sfn_num_rec + * Request: Number of learned notifications and aged-out notification + * records requested. + * Response: Number of notification records returned (must be smaller + * than or equal to the value requested) + * Ranges 0..64 + * Access: OP + */ +MLXSW_ITEM32(reg, sfn, num_rec, 0x04, 0, 8); + +static inline void mlxsw_reg_sfn_pack(char *payload) +{ + MLXSW_REG_ZERO(sfn, payload); + mlxsw_reg_sfn_swid_set(payload, 0); + mlxsw_reg_sfn_num_rec_set(payload, MLXSW_REG_SFN_REC_MAX_COUNT); +} + +/* reg_sfn_rec_swid + * Switch partition ID. + * Access: RO + */ +MLXSW_ITEM32_INDEXED(reg, sfn, rec_swid, MLXSW_REG_SFN_BASE_LEN, 24, 8, + MLXSW_REG_SFN_REC_LEN, 0x00, false); + +enum mlxsw_reg_sfn_rec_type { + /* MAC addresses learned on a regular port. */ + MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC = 0x5, + /* Aged-out MAC address on a regular port */ + MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC = 0x7, +}; + +/* reg_sfn_rec_type + * Notification record type. + * Access: RO + */ +MLXSW_ITEM32_INDEXED(reg, sfn, rec_type, MLXSW_REG_SFN_BASE_LEN, 20, 4, + MLXSW_REG_SFN_REC_LEN, 0x00, false); + +/* reg_sfn_rec_mac + * MAC address. + * Access: RO + */ +MLXSW_ITEM_BUF_INDEXED(reg, sfn, rec_mac, MLXSW_REG_SFN_BASE_LEN, 6, + MLXSW_REG_SFN_REC_LEN, 0x02); + +/* reg_sfd_mac_sub_port + * VEPA channel on the local port. + * 0 if multichannel VEPA is not enabled. + * Access: RO + */ +MLXSW_ITEM32_INDEXED(reg, sfn, mac_sub_port, MLXSW_REG_SFN_BASE_LEN, 16, 8, + MLXSW_REG_SFN_REC_LEN, 0x08, false); + +/* reg_sfd_mac_fid + * Filtering identifier. + * Access: RO + */ +MLXSW_ITEM32_INDEXED(reg, sfn, mac_fid, MLXSW_REG_SFN_BASE_LEN, 0, 16, + MLXSW_REG_SFN_REC_LEN, 0x08, false); + +/* reg_sfd_mac_system_port + * Unique port identifier for the final destination of the packet. + * Access: RO + */ +MLXSW_ITEM32_INDEXED(reg, sfn, mac_system_port, MLXSW_REG_SFN_BASE_LEN, 0, 16, + MLXSW_REG_SFN_REC_LEN, 0x0C, false); + +static inline void mlxsw_reg_sfn_mac_unpack(char *payload, int rec_index, + char *mac, u16 *p_vid, + u8 *p_local_port) +{ + mlxsw_reg_sfn_rec_mac_memcpy_from(payload, rec_index, mac); + *p_vid = mlxsw_reg_sfn_mac_fid_get(payload, rec_index); + *p_local_port = mlxsw_reg_sfn_mac_system_port_get(payload, rec_index); +} + /* SPMS - Switch Port MSTP/RSTP State Register * ------------------------------------------- * Configures the spanning tree state of a physical port. @@ -1467,6 +1566,8 @@ static inline const char *mlxsw_reg_id_str(u16 reg_id) return "SSPR"; case MLXSW_REG_SFD_ID: return "SFD"; + case MLXSW_REG_SFN_ID: + return "SFN"; case MLXSW_REG_SPMS_ID: return "SPMS"; case MLXSW_REG_SFGC_ID: -- cgit v0.10.2 From b2e345f9a454f6e435f1a61b02f0e4f0125b94e3 Mon Sep 17 00:00:00 2001 From: Elad Raz Date: Fri, 16 Oct 2015 14:01:30 +0200 Subject: mlxsw: reg: Add Switch Port VID and Switch Port VLAN Membership registers definitions Add SPVID and SPVM registers responsible for default port VID configuration and VLAN membership of a port. Signed-off-by: Elad Raz Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 8afa2b6..f23f951 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -517,6 +517,148 @@ static inline void mlxsw_reg_spms_vid_pack(char *payload, u16 vid, mlxsw_reg_spms_state_set(payload, vid, state); } +/* SPVID - Switch Port VID + * ----------------------- + * The switch port VID configures the default VID for a port. + */ +#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, +}; + +/* reg_spvid_local_port + * Local port number. + * Access: Index + */ +MLXSW_ITEM32(reg, spvid, local_port, 0x00, 16, 8); + +/* reg_spvid_sub_port + * Virtual port within the physical port. + * Should be set to 0 when virtual ports are not enabled on the port. + * Access: Index + */ +MLXSW_ITEM32(reg, spvid, sub_port, 0x00, 8, 8); + +/* reg_spvid_pvid + * Port default VID + * Access: RW + */ +MLXSW_ITEM32(reg, spvid, pvid, 0x04, 0, 12); + +static inline void mlxsw_reg_spvid_pack(char *payload, u8 local_port, u16 pvid) +{ + MLXSW_REG_ZERO(spvid, payload); + mlxsw_reg_spvid_local_port_set(payload, local_port); + mlxsw_reg_spvid_pvid_set(payload, pvid); +} + +/* SPVM - Switch Port VLAN Membership + * ---------------------------------- + * The Switch Port VLAN Membership register configures the VLAN membership + * of a port in a VLAN denoted by VID. VLAN membership is managed per + * virtual port. The register can be used to add and remove VID(s) from a port. + */ +#define MLXSW_REG_SPVM_ID 0x200F +#define MLXSW_REG_SPVM_BASE_LEN 0x04 /* base length, without records */ +#define MLXSW_REG_SPVM_REC_LEN 0x04 /* record length */ +#define MLXSW_REG_SPVM_REC_MAX_COUNT 256 +#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, +}; + +/* reg_spvm_pt + * Priority tagged. If this bit is set, packets forwarded to the port with + * untagged VLAN membership (u bit is set) will be tagged with priority tag + * (VID=0) + * Access: RW + */ +MLXSW_ITEM32(reg, spvm, pt, 0x00, 31, 1); + +/* reg_spvm_pte + * Priority Tagged Update Enable. On Write operations, if this bit is cleared, + * the pt bit will NOT be updated. To update the pt bit, pte must be set. + * Access: WO + */ +MLXSW_ITEM32(reg, spvm, pte, 0x00, 30, 1); + +/* reg_spvm_local_port + * Local port number. + * Access: Index + */ +MLXSW_ITEM32(reg, spvm, local_port, 0x00, 16, 8); + +/* reg_spvm_sub_port + * Virtual port within the physical port. + * Should be set to 0 when virtual ports are not enabled on the port. + * Access: Index + */ +MLXSW_ITEM32(reg, spvm, sub_port, 0x00, 8, 8); + +/* reg_spvm_num_rec + * Number of records to update. Each record contains: i, e, u, vid. + * Access: OP + */ +MLXSW_ITEM32(reg, spvm, num_rec, 0x00, 0, 8); + +/* reg_spvm_rec_i + * Ingress membership in VLAN ID. + * Access: Index + */ +MLXSW_ITEM32_INDEXED(reg, spvm, rec_i, + MLXSW_REG_SPVM_BASE_LEN, 14, 1, + MLXSW_REG_SPVM_REC_LEN, 0, false); + +/* reg_spvm_rec_e + * Egress membership in VLAN ID. + * Access: Index + */ +MLXSW_ITEM32_INDEXED(reg, spvm, rec_e, + MLXSW_REG_SPVM_BASE_LEN, 13, 1, + MLXSW_REG_SPVM_REC_LEN, 0, false); + +/* reg_spvm_rec_u + * Untagged - port is an untagged member - egress transmission uses untagged + * frames on VID + * Access: Index + */ +MLXSW_ITEM32_INDEXED(reg, spvm, rec_u, + MLXSW_REG_SPVM_BASE_LEN, 12, 1, + MLXSW_REG_SPVM_REC_LEN, 0, false); + +/* reg_spvm_rec_vid + * Egress membership in VLAN ID. + * Access: Index + */ +MLXSW_ITEM32_INDEXED(reg, spvm, rec_vid, + MLXSW_REG_SPVM_BASE_LEN, 0, 12, + MLXSW_REG_SPVM_REC_LEN, 0, false); + +static inline void mlxsw_reg_spvm_pack(char *payload, u8 local_port, + u16 vid_begin, u16 vid_end, + bool is_member, bool untagged) +{ + int size = vid_end - vid_begin + 1; + int i; + + MLXSW_REG_ZERO(spvm, payload); + mlxsw_reg_spvm_local_port_set(payload, local_port); + mlxsw_reg_spvm_num_rec_set(payload, size); + + for (i = 0; i < size; i++) { + mlxsw_reg_spvm_rec_i_set(payload, i, is_member); + mlxsw_reg_spvm_rec_e_set(payload, i, is_member); + mlxsw_reg_spvm_rec_u_set(payload, i, untagged); + mlxsw_reg_spvm_rec_vid_set(payload, i, vid_begin + i); + } +} + /* SFGC - Switch Flooding Group Configuration * ------------------------------------------ * The following register controls the association of flooding tables and MIDs @@ -1570,6 +1712,10 @@ static inline const char *mlxsw_reg_id_str(u16 reg_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_SFGC_ID: return "SFGC"; case MLXSW_REG_SFTR_ID: -- cgit v0.10.2 From e059436999d797bbdb36ec41b6f6890a569cbf94 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 16 Oct 2015 14:01:31 +0200 Subject: mlxsw: reg: Add shared buffer configuration registers definitions Add definitions of SBPR, SBCM, SBPM, SBMM and PBMC registers that are used to configure shared buffers. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index f23f951..bb0ad09 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -1418,6 +1418,82 @@ static inline void mlxsw_reg_ppcnt_pack(char *payload, u8 local_port) mlxsw_reg_ppcnt_prio_tc_set(payload, 0); } +/* PBMC - Port Buffer Management Control Register + * ---------------------------------------------- + * The PBMC register configures and retrieves the port packet buffer + * allocation for different Prios, and the Pause threshold management. + */ +#define MLXSW_REG_PBMC_ID 0x500C +#define MLXSW_REG_PBMC_LEN 0x68 + +static const struct mlxsw_reg_info mlxsw_reg_pbmc = { + .id = MLXSW_REG_PBMC_ID, + .len = MLXSW_REG_PBMC_LEN, +}; + +/* reg_pbmc_local_port + * Local port number. + * Access: Index + */ +MLXSW_ITEM32(reg, pbmc, local_port, 0x00, 16, 8); + +/* reg_pbmc_xoff_timer_value + * When device generates a pause frame, it uses this value as the pause + * timer (time for the peer port to pause in quota-512 bit time). + * Access: RW + */ +MLXSW_ITEM32(reg, pbmc, xoff_timer_value, 0x04, 16, 16); + +/* reg_pbmc_xoff_refresh + * The time before a new pause frame should be sent to refresh the pause RW + * state. Using the same units as xoff_timer_value above (in quota-512 bit + * time). + * Access: RW + */ +MLXSW_ITEM32(reg, pbmc, xoff_refresh, 0x04, 0, 16); + +/* reg_pbmc_buf_lossy + * The field indicates if the buffer is lossy. + * 0 - Lossless + * 1 - Lossy + * Access: RW + */ +MLXSW_ITEM32_INDEXED(reg, pbmc, buf_lossy, 0x0C, 25, 1, 0x08, 0x00, false); + +/* reg_pbmc_buf_epsb + * Eligible for Port Shared buffer. + * If epsb is set, packets assigned to buffer are allowed to insert the port + * shared buffer. + * When buf_lossy is MLXSW_REG_PBMC_LOSSY_LOSSY this field is reserved. + * Access: RW + */ +MLXSW_ITEM32_INDEXED(reg, pbmc, buf_epsb, 0x0C, 24, 1, 0x08, 0x00, false); + +/* reg_pbmc_buf_size + * The part of the packet buffer array is allocated for the specific buffer. + * Units are represented in cells. + * Access: RW + */ +MLXSW_ITEM32_INDEXED(reg, pbmc, buf_size, 0x0C, 0, 16, 0x08, 0x00, false); + +static inline void mlxsw_reg_pbmc_pack(char *payload, u8 local_port, + u16 xoff_timer_value, u16 xoff_refresh) +{ + MLXSW_REG_ZERO(pbmc, payload); + mlxsw_reg_pbmc_local_port_set(payload, local_port); + mlxsw_reg_pbmc_xoff_timer_value_set(payload, xoff_timer_value); + mlxsw_reg_pbmc_xoff_refresh_set(payload, xoff_refresh); +} + +static inline void mlxsw_reg_pbmc_lossy_buffer_pack(char *payload, + int buf_index, + u16 size) +{ + mlxsw_reg_pbmc_buf_lossy_set(payload, buf_index, 1); + mlxsw_reg_pbmc_buf_epsb_set(payload, buf_index, 0); + mlxsw_reg_pbmc_buf_size_set(payload, buf_index, size); +} + /* PSPA - Port Switch Partition Allocation * --------------------------------------- * Controls the association of a port with a switch partition and enables @@ -1697,6 +1773,269 @@ static inline void mlxsw_reg_hpkt_pack(char *payload, u8 action, u16 trap_id) mlxsw_reg_hpkt_ctrl_set(payload, MLXSW_REG_HPKT_CTRL_PACKET_DEFAULT); } +/* SBPR - Shared Buffer Pools Register + * ----------------------------------- + * The SBPR configures and retrieves the shared buffer pools and configuration. + */ +#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, +}; + +enum mlxsw_reg_sbpr_dir { + MLXSW_REG_SBPR_DIR_INGRESS, + MLXSW_REG_SBPR_DIR_EGRESS, +}; + +/* reg_sbpr_dir + * Direction. + * Access: Index + */ +MLXSW_ITEM32(reg, sbpr, dir, 0x00, 24, 2); + +/* reg_sbpr_pool + * Pool index. + * Access: Index + */ +MLXSW_ITEM32(reg, sbpr, pool, 0x00, 0, 4); + +/* reg_sbpr_size + * Pool size in buffer cells. + * Access: RW + */ +MLXSW_ITEM32(reg, sbpr, size, 0x04, 0, 24); + +enum mlxsw_reg_sbpr_mode { + MLXSW_REG_SBPR_MODE_STATIC, + MLXSW_REG_SBPR_MODE_DYNAMIC, +}; + +/* reg_sbpr_mode + * Pool quota calculation mode. + * Access: RW + */ +MLXSW_ITEM32(reg, sbpr, mode, 0x08, 0, 4); + +static inline void mlxsw_reg_sbpr_pack(char *payload, u8 pool, + enum mlxsw_reg_sbpr_dir dir, + enum mlxsw_reg_sbpr_mode mode, u32 size) +{ + MLXSW_REG_ZERO(sbpr, payload); + mlxsw_reg_sbpr_pool_set(payload, pool); + mlxsw_reg_sbpr_dir_set(payload, dir); + mlxsw_reg_sbpr_mode_set(payload, mode); + mlxsw_reg_sbpr_size_set(payload, size); +} + +/* SBCM - Shared Buffer Class Management Register + * ---------------------------------------------- + * The SBCM register configures and retrieves the shared buffer allocation + * and configuration according to Port-PG, including the binding to pool + * and definition of the associated quota. + */ +#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, +}; + +/* reg_sbcm_local_port + * Local port number. + * For Ingress: excludes CPU port and Router port + * For Egress: excludes IP Router + * Access: Index + */ +MLXSW_ITEM32(reg, sbcm, local_port, 0x00, 16, 8); + +/* reg_sbcm_pg_buff + * PG buffer - Port PG (dir=ingress) / traffic class (dir=egress) + * For PG buffer: range is 0..cap_max_pg_buffers - 1 + * For traffic class: range is 0..cap_max_tclass - 1 + * Note that when traffic class is in MC aware mode then the traffic + * classes which are MC aware cannot be configured. + * Access: Index + */ +MLXSW_ITEM32(reg, sbcm, pg_buff, 0x00, 8, 6); + +enum mlxsw_reg_sbcm_dir { + MLXSW_REG_SBCM_DIR_INGRESS, + MLXSW_REG_SBCM_DIR_EGRESS, +}; + +/* reg_sbcm_dir + * Direction. + * Access: Index + */ +MLXSW_ITEM32(reg, sbcm, dir, 0x00, 0, 2); + +/* reg_sbcm_min_buff + * Minimum buffer size for the limiter, in cells. + * Access: RW + */ +MLXSW_ITEM32(reg, sbcm, min_buff, 0x18, 0, 24); + +/* reg_sbcm_max_buff + * When the pool associated to the port-pg/tclass is configured to + * static, Maximum buffer size for the limiter configured in cells. + * When the pool associated to the port-pg/tclass is configured to + * dynamic, the max_buff holds the "alpha" parameter, supporting + * the following values: + * 0: 0 + * i: (1/128)*2^(i-1), for i=1..14 + * 0xFF: Infinity + * Access: RW + */ +MLXSW_ITEM32(reg, sbcm, max_buff, 0x1C, 0, 24); + +/* reg_sbcm_pool + * Association of the port-priority to a pool. + * Access: RW + */ +MLXSW_ITEM32(reg, sbcm, pool, 0x24, 0, 4); + +static inline void mlxsw_reg_sbcm_pack(char *payload, u8 local_port, u8 pg_buff, + enum mlxsw_reg_sbcm_dir dir, + u32 min_buff, u32 max_buff, u8 pool) +{ + MLXSW_REG_ZERO(sbcm, payload); + mlxsw_reg_sbcm_local_port_set(payload, local_port); + mlxsw_reg_sbcm_pg_buff_set(payload, pg_buff); + mlxsw_reg_sbcm_dir_set(payload, dir); + mlxsw_reg_sbcm_min_buff_set(payload, min_buff); + mlxsw_reg_sbcm_max_buff_set(payload, max_buff); + mlxsw_reg_sbcm_pool_set(payload, pool); +} + +/* SBPM - Shared Buffer Class Management Register + * ---------------------------------------------- + * The SBPM register configures and retrieves the shared buffer allocation + * and configuration according to Port-Pool, including the definition + * of the associated quota. + */ +#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, +}; + +/* reg_sbpm_local_port + * Local port number. + * For Ingress: excludes CPU port and Router port + * For Egress: excludes IP Router + * Access: Index + */ +MLXSW_ITEM32(reg, sbpm, local_port, 0x00, 16, 8); + +/* reg_sbpm_pool + * The pool associated to quota counting on the local_port. + * Access: Index + */ +MLXSW_ITEM32(reg, sbpm, pool, 0x00, 8, 4); + +enum mlxsw_reg_sbpm_dir { + MLXSW_REG_SBPM_DIR_INGRESS, + MLXSW_REG_SBPM_DIR_EGRESS, +}; + +/* reg_sbpm_dir + * Direction. + * Access: Index + */ +MLXSW_ITEM32(reg, sbpm, dir, 0x00, 0, 2); + +/* reg_sbpm_min_buff + * Minimum buffer size for the limiter, in cells. + * Access: RW + */ +MLXSW_ITEM32(reg, sbpm, min_buff, 0x18, 0, 24); + +/* reg_sbpm_max_buff + * When the pool associated to the port-pg/tclass is configured to + * static, Maximum buffer size for the limiter configured in cells. + * When the pool associated to the port-pg/tclass is configured to + * dynamic, the max_buff holds the "alpha" parameter, supporting + * the following values: + * 0: 0 + * i: (1/128)*2^(i-1), for i=1..14 + * 0xFF: Infinity + * Access: RW + */ +MLXSW_ITEM32(reg, sbpm, max_buff, 0x1C, 0, 24); + +static inline void mlxsw_reg_sbpm_pack(char *payload, u8 local_port, u8 pool, + enum mlxsw_reg_sbpm_dir dir, + u32 min_buff, u32 max_buff) +{ + MLXSW_REG_ZERO(sbpm, payload); + mlxsw_reg_sbpm_local_port_set(payload, local_port); + mlxsw_reg_sbpm_pool_set(payload, pool); + mlxsw_reg_sbpm_dir_set(payload, dir); + mlxsw_reg_sbpm_min_buff_set(payload, min_buff); + mlxsw_reg_sbpm_max_buff_set(payload, max_buff); +} + +/* SBMM - Shared Buffer Multicast Management Register + * -------------------------------------------------- + * The SBMM register configures and retrieves the shared buffer allocation + * and configuration for MC packets according to Switch-Priority, including + * the binding to pool and definition of the associated quota. + */ +#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, +}; + +/* reg_sbmm_prio + * Switch Priority. + * Access: Index + */ +MLXSW_ITEM32(reg, sbmm, prio, 0x00, 8, 4); + +/* reg_sbmm_min_buff + * Minimum buffer size for the limiter, in cells. + * Access: RW + */ +MLXSW_ITEM32(reg, sbmm, min_buff, 0x18, 0, 24); + +/* reg_sbmm_max_buff + * When the pool associated to the port-pg/tclass is configured to + * static, Maximum buffer size for the limiter configured in cells. + * When the pool associated to the port-pg/tclass is configured to + * dynamic, the max_buff holds the "alpha" parameter, supporting + * the following values: + * 0: 0 + * i: (1/128)*2^(i-1), for i=1..14 + * 0xFF: Infinity + * Access: RW + */ +MLXSW_ITEM32(reg, sbmm, max_buff, 0x1C, 0, 24); + +/* reg_sbmm_pool + * Association of the port-priority to a pool. + * Access: RW + */ +MLXSW_ITEM32(reg, sbmm, pool, 0x24, 0, 4); + +static inline void mlxsw_reg_sbmm_pack(char *payload, u8 prio, u32 min_buff, + u32 max_buff, u8 pool) +{ + MLXSW_REG_ZERO(sbmm, payload); + mlxsw_reg_sbmm_prio_set(payload, prio); + mlxsw_reg_sbmm_min_buff_set(payload, min_buff); + mlxsw_reg_sbmm_max_buff_set(payload, max_buff); + mlxsw_reg_sbmm_pool_set(payload, pool); +} + static inline const char *mlxsw_reg_id_str(u16 reg_id) { switch (reg_id) { @@ -1734,12 +2073,22 @@ static inline const char *mlxsw_reg_id_str(u16 reg_id) return "PAOS"; case MLXSW_REG_PPCNT_ID: return "PPCNT"; + 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_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"; default: return "*UNKNOWN*"; } -- cgit v0.10.2 From f1fb693a0875d0d1510cf8e84d35df2661f5a38f Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 16 Oct 2015 14:01:32 +0200 Subject: mlxsw: reg: Add Switch FID Management register definition Filtering identifiers (FIDs) are unique identifers of bridge instances in the hardware. Add the SFMR register, which is responsible for the creation and configuration of these FIDs. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index bb0ad09..6f4a7b0 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -883,6 +883,91 @@ static inline void mlxsw_reg_spmlr_pack(char *payload, u8 local_port, mlxsw_reg_spmlr_learn_mode_set(payload, mode); } +/* SFMR - Switch FID Management Register + * ------------------------------------- + * Creates and configures FIDs. + */ +#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, +}; + +enum mlxsw_reg_sfmr_op { + MLXSW_REG_SFMR_OP_CREATE_FID, + MLXSW_REG_SFMR_OP_DESTROY_FID, +}; + +/* reg_sfmr_op + * Operation. + * 0 - Create or edit FID. + * 1 - Destroy FID. + * Access: WO + */ +MLXSW_ITEM32(reg, sfmr, op, 0x00, 24, 4); + +/* reg_sfmr_fid + * Filtering ID. + * Access: Index + */ +MLXSW_ITEM32(reg, sfmr, fid, 0x00, 0, 16); + +/* reg_sfmr_fid_offset + * FID offset. + * Used to point into the flooding table selected by SFGC register if + * the table is of type FID-Offset. Otherwise, this field is reserved. + * Access: RW + */ +MLXSW_ITEM32(reg, sfmr, fid_offset, 0x08, 0, 16); + +/* reg_sfmr_vtfp + * Valid Tunnel Flood Pointer. + * If not set, then nve_tunnel_flood_ptr is reserved and considered NULL. + * Access: RW + * + * Note: Reserved for 802.1Q FIDs. + */ +MLXSW_ITEM32(reg, sfmr, vtfp, 0x0C, 31, 1); + +/* reg_sfmr_nve_tunnel_flood_ptr + * Underlay Flooding and BC Pointer. + * Used as a pointer to the first entry of the group based link lists of + * flooding or BC entries (for NVE tunnels). + * Access: RW + */ +MLXSW_ITEM32(reg, sfmr, nve_tunnel_flood_ptr, 0x0C, 0, 24); + +/* reg_sfmr_vv + * VNI Valid. + * If not set, then vni is reserved. + * Access: RW + * + * Note: Reserved for 802.1Q FIDs. + */ +MLXSW_ITEM32(reg, sfmr, vv, 0x10, 31, 1); + +/* reg_sfmr_vni + * Virtual Network Identifier. + * Access: RW + * + * Note: A given VNI can only be assigned to one FID. + */ +MLXSW_ITEM32(reg, sfmr, vni, 0x10, 0, 24); + +static inline void mlxsw_reg_sfmr_pack(char *payload, + enum mlxsw_reg_sfmr_op op, u16 fid, + u16 fid_offset) +{ + MLXSW_REG_ZERO(sfmr, payload); + mlxsw_reg_sfmr_op_set(payload, op); + mlxsw_reg_sfmr_fid_set(payload, fid); + mlxsw_reg_sfmr_fid_offset_set(payload, fid_offset); + mlxsw_reg_sfmr_vtfp_set(payload, false); + mlxsw_reg_sfmr_vv_set(payload, false); +} + /* PMLP - Ports Module to Local Port Register * ------------------------------------------ * Configures the assignment of modules to local ports. @@ -2061,6 +2146,8 @@ static inline const char *mlxsw_reg_id_str(u16 reg_id) return "SFTR"; case MLXSW_REG_SPMLR_ID: return "SPMLR"; + case MLXSW_REG_SFMR_ID: + return "SFMR"; case MLXSW_REG_PMLP_ID: return "PMLP"; case MLXSW_REG_PMTU_ID: -- cgit v0.10.2 From 647902397663468c2feefa9cd8097e194802a0b3 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 16 Oct 2015 14:01:33 +0200 Subject: mlxsw: reg: Add Switch VID to FID Allocation register definition An incoming packet can be classified into a filtering identifer (FID) based on its VID or incoming port and VID ({Port, VID}). Add the SVFA register, which controls this mapping. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 6f4a7b0..5a97707 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -883,6 +883,99 @@ static inline void mlxsw_reg_spmlr_pack(char *payload, u8 local_port, mlxsw_reg_spmlr_learn_mode_set(payload, mode); } +/* SVFA - Switch VID to FID Allocation Register + * -------------------------------------------- + * Controls the VID to FID mapping and {Port, VID} to FID mapping for + * virtualized ports. + */ +#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, +}; + +/* reg_svfa_swid + * Switch partition ID. + * Access: Index + */ +MLXSW_ITEM32(reg, svfa, swid, 0x00, 24, 8); + +/* reg_svfa_local_port + * Local port number. + * Access: Index + * + * Note: Reserved for 802.1Q FIDs. + */ +MLXSW_ITEM32(reg, svfa, local_port, 0x00, 16, 8); + +enum mlxsw_reg_svfa_mt { + MLXSW_REG_SVFA_MT_VID_TO_FID, + MLXSW_REG_SVFA_MT_PORT_VID_TO_FID, +}; + +/* reg_svfa_mapping_table + * Mapping table: + * 0 - VID to FID + * 1 - {Port, VID} to FID + * Access: Index + * + * Note: Reserved for SwitchX-2. + */ +MLXSW_ITEM32(reg, svfa, mapping_table, 0x00, 8, 3); + +/* reg_svfa_v + * Valid. + * Valid if set. + * Access: RW + * + * Note: Reserved for SwitchX-2. + */ +MLXSW_ITEM32(reg, svfa, v, 0x00, 0, 1); + +/* reg_svfa_fid + * Filtering ID. + * Access: RW + */ +MLXSW_ITEM32(reg, svfa, fid, 0x04, 16, 16); + +/* reg_svfa_vid + * VLAN ID. + * Access: Index + */ +MLXSW_ITEM32(reg, svfa, vid, 0x04, 0, 12); + +/* reg_svfa_counter_set_type + * Counter set type for flow counters. + * Access: RW + * + * Note: Reserved for SwitchX-2. + */ +MLXSW_ITEM32(reg, svfa, counter_set_type, 0x08, 24, 8); + +/* reg_svfa_counter_index + * Counter index for flow counters. + * Access: RW + * + * Note: Reserved for SwitchX-2. + */ +MLXSW_ITEM32(reg, svfa, counter_index, 0x08, 0, 24); + +static inline void mlxsw_reg_svfa_pack(char *payload, u8 local_port, + enum mlxsw_reg_svfa_mt mt, bool valid, + u16 fid, u16 vid) +{ + MLXSW_REG_ZERO(svfa, payload); + local_port = mt == MLXSW_REG_SVFA_MT_VID_TO_FID ? 0 : local_port; + mlxsw_reg_svfa_swid_set(payload, 0); + mlxsw_reg_svfa_local_port_set(payload, local_port); + mlxsw_reg_svfa_mapping_table_set(payload, mt); + mlxsw_reg_svfa_v_set(payload, valid); + mlxsw_reg_svfa_fid_set(payload, fid); + mlxsw_reg_svfa_vid_set(payload, vid); +} + /* SFMR - Switch FID Management Register * ------------------------------------- * Creates and configures FIDs. @@ -2146,6 +2239,8 @@ static inline const char *mlxsw_reg_id_str(u16 reg_id) return "SFTR"; case MLXSW_REG_SPMLR_ID: return "SPMLR"; + case MLXSW_REG_SVFA_ID: + return "SVFA"; case MLXSW_REG_SFMR_ID: return "SFMR"; case MLXSW_REG_PMLP_ID: -- cgit v0.10.2 From 1f65da742d15462786533cf3ea58335aa32e3daa Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 16 Oct 2015 14:01:34 +0200 Subject: mlxsw: reg: Add Switch Virtual-Port Enabling register definition In order for a port to support {Port, VID} to FID mapping it needs to be configured to a virtual port mode (as opposed to VLAN mode). Add the SVPE register, which enables port virtualization. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 5a97707..8e1888b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -976,6 +976,42 @@ static inline void mlxsw_reg_svfa_pack(char *payload, u8 local_port, mlxsw_reg_svfa_vid_set(payload, vid); } +/* SVPE - Switch Virtual-Port Enabling Register + * -------------------------------------------- + * Enables port virtualization. + */ +#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, +}; + +/* reg_svpe_local_port + * Local port number + * Access: Index + * + * Note: CPU port is not supported (uses VLAN mode only). + */ +MLXSW_ITEM32(reg, svpe, local_port, 0x00, 16, 8); + +/* reg_svpe_vp_en + * Virtual port enable. + * 0 - Disable, VLAN mode (VID to FID). + * 1 - Enable, Virtual port mode ({Port, VID} to FID). + * Access: RW + */ +MLXSW_ITEM32(reg, svpe, vp_en, 0x00, 8, 1); + +static inline void mlxsw_reg_svpe_pack(char *payload, u8 local_port, + bool enable) +{ + MLXSW_REG_ZERO(svpe, payload); + mlxsw_reg_svpe_local_port_set(payload, local_port); + mlxsw_reg_svpe_vp_en_set(payload, enable); +} + /* SFMR - Switch FID Management Register * ------------------------------------- * Creates and configures FIDs. @@ -2241,6 +2277,8 @@ static inline const char *mlxsw_reg_id_str(u16 reg_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_PMLP_ID: -- cgit v0.10.2 From e534a56a317fa4e55419cc255d6af2683288d71c Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 16 Oct 2015 14:01:35 +0200 Subject: mlxsw: reg: Add Switch Filtering Database Aging Time register definition Add SFDAT which is used to control switch ageing time. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 8e1888b..d1c8eff 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -157,6 +157,41 @@ static inline void mlxsw_reg_sspr_pack(char *payload, u8 local_port) mlxsw_reg_sspr_system_port_set(payload, local_port); } +/* SFDAT - Switch Filtering Database Aging Time + * -------------------------------------------- + * Controls the Switch aging time. Aging time is able to be set per Switch + * Partition. + */ +#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, +}; + +/* reg_sfdat_swid + * Switch partition ID. + * Access: Index + */ +MLXSW_ITEM32(reg, sfdat, swid, 0x00, 24, 8); + +/* reg_sfdat_age_time + * Aging time in seconds + * Min - 10 seconds + * Max - 1,000,000 seconds + * Default is 300 seconds. + * Access: RW + */ +MLXSW_ITEM32(reg, sfdat, age_time, 0x04, 0, 20); + +static inline void mlxsw_reg_sfdat_pack(char *payload, u32 age_time) +{ + MLXSW_REG_ZERO(sfdat, payload); + mlxsw_reg_sfdat_swid_set(payload, 0); + mlxsw_reg_sfdat_age_time_set(payload, age_time); +} + /* SFD - Switch Filtering Database * ------------------------------- * The following register defines the access to the filtering database. @@ -2259,6 +2294,8 @@ static inline const char *mlxsw_reg_id_str(u16 reg_id) return "SPAD"; 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: -- cgit v0.10.2 From a4feea74cd7a8f12d9fae67014533a4220135760 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 16 Oct 2015 14:01:36 +0200 Subject: mlxsw: reg: Add Switch Port VLAN MAC Learning register definition Since we currently do not support the offloading of 802.1D bridges, we need to be able to let the device know it should not learn MAC addresses on specific {Port, VID} pairs. Add the SPVMLR register, which controls the learning enablement of {Port, VID} pairs. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index d1c8eff..4fcba46 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -1132,6 +1132,71 @@ static inline void mlxsw_reg_sfmr_pack(char *payload, mlxsw_reg_sfmr_vv_set(payload, false); } +/* SPVMLR - Switch Port VLAN MAC Learning Register + * ----------------------------------------------- + * Controls the switch MAC learning policy per {Port, VID}. + */ +#define MLXSW_REG_SPVMLR_ID 0x2020 +#define MLXSW_REG_SPVMLR_BASE_LEN 0x04 /* base length, without records */ +#define MLXSW_REG_SPVMLR_REC_LEN 0x04 /* record length */ +#define MLXSW_REG_SPVMLR_REC_MAX_COUNT 256 +#define MLXSW_REG_SPVMLR_LEN (MLXSW_REG_SPVMLR_BASE_LEN + \ + 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, +}; + +/* reg_spvmlr_local_port + * Local ingress port. + * Access: Index + * + * Note: CPU port is not supported. + */ +MLXSW_ITEM32(reg, spvmlr, local_port, 0x00, 16, 8); + +/* reg_spvmlr_num_rec + * Number of records to update. + * Access: OP + */ +MLXSW_ITEM32(reg, spvmlr, num_rec, 0x00, 0, 8); + +/* reg_spvmlr_rec_learn_enable + * 0 - Disable learning for {Port, VID}. + * 1 - Enable learning for {Port, VID}. + * Access: RW + */ +MLXSW_ITEM32_INDEXED(reg, spvmlr, rec_learn_enable, MLXSW_REG_SPVMLR_BASE_LEN, + 31, 1, MLXSW_REG_SPVMLR_REC_LEN, 0x00, false); + +/* reg_spvmlr_rec_vid + * VLAN ID to be added/removed from port or for querying. + * Access: Index + */ +MLXSW_ITEM32_INDEXED(reg, spvmlr, rec_vid, MLXSW_REG_SPVMLR_BASE_LEN, 0, 12, + MLXSW_REG_SPVMLR_REC_LEN, 0x00, false); + +static inline void mlxsw_reg_spvmlr_pack(char *payload, u8 local_port, + u16 vid_begin, u16 vid_end, + bool learn_enable) +{ + int num_rec = vid_end - vid_begin + 1; + int i; + + WARN_ON(num_rec < 1 || num_rec > MLXSW_REG_SPVMLR_REC_MAX_COUNT); + + MLXSW_REG_ZERO(spvmlr, payload); + mlxsw_reg_spvmlr_local_port_set(payload, local_port); + mlxsw_reg_spvmlr_num_rec_set(payload, num_rec); + + for (i = 0; i < num_rec; i++) { + mlxsw_reg_spvmlr_rec_learn_enable_set(payload, i, learn_enable); + mlxsw_reg_spvmlr_rec_vid_set(payload, i, vid_begin + i); + } +} + /* PMLP - Ports Module to Local Port Register * ------------------------------------------ * Configures the assignment of modules to local ports. @@ -2318,6 +2383,8 @@ static inline const char *mlxsw_reg_id_str(u16 reg_id) return "SVPE"; case MLXSW_REG_SFMR_ID: return "SFMR"; + case MLXSW_REG_SPVMLR_ID: + return "SPVMLR"; case MLXSW_REG_PMLP_ID: return "PMLP"; case MLXSW_REG_PMTU_ID: -- cgit v0.10.2 From 56ade8fe3fe1e134783f61d164305107ae01030f Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 16 Oct 2015 14:01:37 +0200 Subject: mlxsw: spectrum: Add initial support for Spectrum ASIC Add support for new generation Mellanox Spectrum ASIC, 10/25/40/50 and 100Gb/s Ethernet Switch. The initial driver implements bridge forwarding offload including bridge internal VLAN support, FDB static entries, FDB learning and HW ageing including their setup. Signed-off-by: Jiri Pirko Signed-off-by: Ido Schimmel Signed-off-by: Elad Raz Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlxsw/Kconfig b/drivers/net/ethernet/mellanox/mlxsw/Kconfig index 2941d9c..e36e122 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Kconfig +++ b/drivers/net/ethernet/mellanox/mlxsw/Kconfig @@ -30,3 +30,14 @@ config MLXSW_SWITCHX2 To compile this driver as a module, choose M here: the module will be called mlxsw_switchx2. + +config MLXSW_SPECTRUM + tristate "Mellanox Technologies Spectrum support" + depends on MLXSW_CORE && NET_SWITCHDEV + default m + ---help--- + This driver supports Mellanox Technologies Spectrum Ethernet + Switch ASICs. + + To compile this driver as a module, choose M here: the + module will be called mlxsw_spectrum. diff --git a/drivers/net/ethernet/mellanox/mlxsw/Makefile b/drivers/net/ethernet/mellanox/mlxsw/Makefile index 0a05f65..af01581 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Makefile +++ b/drivers/net/ethernet/mellanox/mlxsw/Makefile @@ -4,3 +4,6 @@ obj-$(CONFIG_MLXSW_PCI) += mlxsw_pci.o mlxsw_pci-objs := pci.o obj-$(CONFIG_MLXSW_SWITCHX2) += mlxsw_switchx2.o mlxsw_switchx2-objs := switchx2.o +obj-$(CONFIG_MLXSW_SPECTRUM) += mlxsw_spectrum.o +mlxsw_spectrum-objs := spectrum.o spectrum_buffers.o \ + spectrum_switchdev.o diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index e92ab27..8078273 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -54,6 +54,7 @@ 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; diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c index 0fa44c8..879e000 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c @@ -57,6 +57,7 @@ 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, } }; @@ -67,6 +68,8 @@ 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(); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.h b/drivers/net/ethernet/mellanox/mlxsw/pci.h index 5b3453b..142f33d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.h +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.h @@ -40,6 +40,7 @@ #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 new file mode 100644 index 0000000..6e9906d --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -0,0 +1,1948 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/spectrum.c + * Copyright (c) 2015 Mellanox Technologies. All rights reserved. + * Copyright (c) 2015 Jiri Pirko + * Copyright (c) 2015 Ido Schimmel + * Copyright (c) 2015 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 +#include +#include +#include + +#include "spectrum.h" +#include "core.h" +#include "reg.h" +#include "port.h" +#include "trap.h" +#include "txheader.h" + +static const char mlxsw_sp_driver_name[] = "mlxsw_spectrum"; +static const char mlxsw_sp_driver_version[] = "1.0"; + +/* tx_hdr_version + * Tx header version. + * Must be set to 1. + */ +MLXSW_ITEM32(tx, hdr, version, 0x00, 28, 4); + +/* tx_hdr_ctl + * Packet control type. + * 0 - Ethernet control (e.g. EMADs, LACP) + * 1 - Ethernet data + */ +MLXSW_ITEM32(tx, hdr, ctl, 0x00, 26, 2); + +/* tx_hdr_proto + * Packet protocol type. Must be set to 1 (Ethernet). + */ +MLXSW_ITEM32(tx, hdr, proto, 0x00, 21, 3); + +/* tx_hdr_rx_is_router + * Packet is sent from the router. Valid for data packets only. + */ +MLXSW_ITEM32(tx, hdr, rx_is_router, 0x00, 19, 1); + +/* tx_hdr_fid_valid + * Indicates if the 'fid' field is valid and should be used for + * forwarding lookup. Valid for data packets only. + */ +MLXSW_ITEM32(tx, hdr, fid_valid, 0x00, 16, 1); + +/* tx_hdr_swid + * Switch partition ID. Must be set to 0. + */ +MLXSW_ITEM32(tx, hdr, swid, 0x00, 12, 3); + +/* tx_hdr_control_tclass + * Indicates if the packet should use the control TClass and not one + * of the data TClasses. + */ +MLXSW_ITEM32(tx, hdr, control_tclass, 0x00, 6, 1); + +/* tx_hdr_etclass + * Egress TClass to be used on the egress device on the egress port. + */ +MLXSW_ITEM32(tx, hdr, etclass, 0x00, 0, 4); + +/* tx_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, hdr, port_mid, 0x04, 16, 16); + +/* tx_hdr_fid + * Forwarding ID used for L2 forwarding lookup. Valid only if 'fid_valid' is + * set, otherwise calculated based on the packet's VID using VID to FID mapping. + * Valid for data packets only. + */ +MLXSW_ITEM32(tx, hdr, fid, 0x08, 0, 16); + +/* tx_hdr_type + * 0 - Data packets + * 6 - Control packets + */ +MLXSW_ITEM32(tx, hdr, type, 0x0C, 0, 4); + +static void mlxsw_sp_txhdr_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_hdr_version_set(txhdr, MLXSW_TXHDR_VERSION_1); + mlxsw_tx_hdr_ctl_set(txhdr, MLXSW_TXHDR_ETH_CTL); + mlxsw_tx_hdr_proto_set(txhdr, MLXSW_TXHDR_PROTO_ETH); + mlxsw_tx_hdr_swid_set(txhdr, 0); + mlxsw_tx_hdr_control_tclass_set(txhdr, 1); + mlxsw_tx_hdr_port_mid_set(txhdr, tx_info->local_port); + mlxsw_tx_hdr_type_set(txhdr, MLXSW_TXHDR_TYPE_CONTROL); +} + +static int mlxsw_sp_base_mac_get(struct mlxsw_sp *mlxsw_sp) +{ + char spad_pl[MLXSW_REG_SPAD_LEN]; + int err; + + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(spad), spad_pl); + if (err) + return err; + mlxsw_reg_spad_base_mac_memcpy_from(spad_pl, mlxsw_sp->base_mac); + return 0; +} + +static int mlxsw_sp_port_admin_status_set(struct mlxsw_sp_port *mlxsw_sp_port, + bool is_up) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char paos_pl[MLXSW_REG_PAOS_LEN]; + + mlxsw_reg_paos_pack(paos_pl, mlxsw_sp_port->local_port, + is_up ? MLXSW_PORT_ADMIN_STATUS_UP : + MLXSW_PORT_ADMIN_STATUS_DOWN); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(paos), paos_pl); +} + +static int mlxsw_sp_port_oper_status_get(struct mlxsw_sp_port *mlxsw_sp_port, + bool *p_is_up) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char paos_pl[MLXSW_REG_PAOS_LEN]; + u8 oper_status; + int err; + + mlxsw_reg_paos_pack(paos_pl, mlxsw_sp_port->local_port, 0); + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(paos), paos_pl); + if (err) + return err; + oper_status = mlxsw_reg_paos_oper_status_get(paos_pl); + *p_is_up = oper_status == MLXSW_PORT_ADMIN_STATUS_UP ? true : false; + return 0; +} + +static int mlxsw_sp_vfid_create(struct mlxsw_sp *mlxsw_sp, u16 vfid) +{ + char sfmr_pl[MLXSW_REG_SFMR_LEN]; + int err; + + mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_CREATE_FID, + MLXSW_SP_VFID_BASE + vfid, 0); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl); + + if (err) + return err; + + set_bit(vfid, mlxsw_sp->active_vfids); + return 0; +} + +static void mlxsw_sp_vfid_destroy(struct mlxsw_sp *mlxsw_sp, u16 vfid) +{ + char sfmr_pl[MLXSW_REG_SFMR_LEN]; + + clear_bit(vfid, mlxsw_sp->active_vfids); + + mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_DESTROY_FID, + MLXSW_SP_VFID_BASE + vfid, 0); + mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl); +} + +static int mlxsw_sp_port_dev_addr_set(struct mlxsw_sp_port *mlxsw_sp_port, + unsigned char *addr) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char ppad_pl[MLXSW_REG_PPAD_LEN]; + + mlxsw_reg_ppad_pack(ppad_pl, true, mlxsw_sp_port->local_port); + mlxsw_reg_ppad_mac_memcpy_to(ppad_pl, addr); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ppad), ppad_pl); +} + +static int mlxsw_sp_port_dev_addr_init(struct mlxsw_sp_port *mlxsw_sp_port) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + unsigned char *addr = mlxsw_sp_port->dev->dev_addr; + + ether_addr_copy(addr, mlxsw_sp->base_mac); + addr[ETH_ALEN - 1] += mlxsw_sp_port->local_port; + return mlxsw_sp_port_dev_addr_set(mlxsw_sp_port, addr); +} + +static int mlxsw_sp_port_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port, + u16 vid, enum mlxsw_reg_spms_state state) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char *spms_pl; + int err; + + spms_pl = kmalloc(MLXSW_REG_SPMS_LEN, GFP_KERNEL); + if (!spms_pl) + return -ENOMEM; + mlxsw_reg_spms_pack(spms_pl, mlxsw_sp_port->local_port); + mlxsw_reg_spms_vid_pack(spms_pl, vid, state); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spms), spms_pl); + kfree(spms_pl); + return err; +} + +static int mlxsw_sp_port_mtu_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 mtu) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + 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_sp_port->local_port, 0); + err = mlxsw_reg_query(mlxsw_sp->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_sp_port->local_port, mtu); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmtu), pmtu_pl); +} + +static int mlxsw_sp_port_swid_set(struct mlxsw_sp_port *mlxsw_sp_port, u8 swid) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char pspa_pl[MLXSW_REG_PSPA_LEN]; + + mlxsw_reg_pspa_pack(pspa_pl, swid, mlxsw_sp_port->local_port); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pspa), pspa_pl); +} + +static int mlxsw_sp_port_vp_mode_set(struct mlxsw_sp_port *mlxsw_sp_port, + bool enable) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char svpe_pl[MLXSW_REG_SVPE_LEN]; + + mlxsw_reg_svpe_pack(svpe_pl, mlxsw_sp_port->local_port, enable); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(svpe), svpe_pl); +} + +int mlxsw_sp_port_vid_to_fid_set(struct mlxsw_sp_port *mlxsw_sp_port, + enum mlxsw_reg_svfa_mt mt, bool valid, u16 fid, + u16 vid) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char svfa_pl[MLXSW_REG_SVFA_LEN]; + + mlxsw_reg_svfa_pack(svfa_pl, mlxsw_sp_port->local_port, mt, valid, + fid, vid); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(svfa), svfa_pl); +} + +static int mlxsw_sp_port_vid_learning_set(struct mlxsw_sp_port *mlxsw_sp_port, + u16 vid, bool learn_enable) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char *spvmlr_pl; + int err; + + spvmlr_pl = kmalloc(MLXSW_REG_SPVMLR_LEN, GFP_KERNEL); + if (!spvmlr_pl) + return -ENOMEM; + mlxsw_reg_spvmlr_pack(spvmlr_pl, mlxsw_sp_port->local_port, vid, vid, + learn_enable); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spvmlr), spvmlr_pl); + kfree(spvmlr_pl); + return err; +} + +static int +mlxsw_sp_port_system_port_mapping_set(struct mlxsw_sp_port *mlxsw_sp_port) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char sspr_pl[MLXSW_REG_SSPR_LEN]; + + mlxsw_reg_sspr_pack(sspr_pl, mlxsw_sp_port->local_port); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sspr), sspr_pl); +} + +static int mlxsw_sp_port_module_check(struct mlxsw_sp_port *mlxsw_sp_port, + bool *p_usable) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char pmlp_pl[MLXSW_REG_PMLP_LEN]; + int err; + + mlxsw_reg_pmlp_pack(pmlp_pl, mlxsw_sp_port->local_port); + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl); + if (err) + return err; + *p_usable = mlxsw_reg_pmlp_width_get(pmlp_pl) ? true : false; + return 0; +} + +static int mlxsw_sp_port_open(struct net_device *dev) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + int err; + + err = mlxsw_sp_port_admin_status_set(mlxsw_sp_port, true); + if (err) + return err; + netif_start_queue(dev); + return 0; +} + +static int mlxsw_sp_port_stop(struct net_device *dev) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + + netif_stop_queue(dev); + return mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false); +} + +static netdev_tx_t mlxsw_sp_port_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + struct mlxsw_sp_port_pcpu_stats *pcpu_stats; + const struct mlxsw_tx_info tx_info = { + .local_port = mlxsw_sp_port->local_port, + .is_emad = false, + }; + u64 len; + int err; + + if (mlxsw_core_skb_transmit_busy(mlxsw_sp, &tx_info)) + return NETDEV_TX_BUSY; + + if (unlikely(skb_headroom(skb) < MLXSW_TXHDR_LEN)) { + struct sk_buff *skb_orig = skb; + + skb = skb_realloc_headroom(skb, MLXSW_TXHDR_LEN); + if (!skb) { + this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped); + dev_kfree_skb_any(skb_orig); + return NETDEV_TX_OK; + } + } + + if (eth_skb_pad(skb)) { + this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped); + return NETDEV_TX_OK; + } + + mlxsw_sp_txhdr_construct(skb, &tx_info); + len = skb->len; + /* Due to a race we might fail here because of a full queue. In that + * unlikely case we simply drop the packet. + */ + err = mlxsw_core_skb_transmit(mlxsw_sp, skb, &tx_info); + + if (!err) { + pcpu_stats = this_cpu_ptr(mlxsw_sp_port->pcpu_stats); + u64_stats_update_begin(&pcpu_stats->syncp); + pcpu_stats->tx_packets++; + pcpu_stats->tx_bytes += len; + u64_stats_update_end(&pcpu_stats->syncp); + } else { + this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped); + dev_kfree_skb_any(skb); + } + return NETDEV_TX_OK; +} + +static int mlxsw_sp_port_set_mac_address(struct net_device *dev, void *p) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct sockaddr *addr = p; + int err; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + err = mlxsw_sp_port_dev_addr_set(mlxsw_sp_port, addr->sa_data); + if (err) + return err; + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + return 0; +} + +static int mlxsw_sp_port_change_mtu(struct net_device *dev, int mtu) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + int err; + + err = mlxsw_sp_port_mtu_set(mlxsw_sp_port, mtu); + if (err) + return err; + dev->mtu = mtu; + return 0; +} + +static struct rtnl_link_stats64 * +mlxsw_sp_port_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp_port_pcpu_stats *p; + u64 rx_packets, rx_bytes, tx_packets, tx_bytes; + u32 tx_dropped = 0; + unsigned int start; + int i; + + for_each_possible_cpu(i) { + p = per_cpu_ptr(mlxsw_sp_port->pcpu_stats, i); + do { + start = u64_stats_fetch_begin_irq(&p->syncp); + rx_packets = p->rx_packets; + rx_bytes = p->rx_bytes; + tx_packets = p->tx_packets; + tx_bytes = p->tx_bytes; + } while (u64_stats_fetch_retry_irq(&p->syncp, start)); + + stats->rx_packets += rx_packets; + stats->rx_bytes += rx_bytes; + stats->tx_packets += tx_packets; + stats->tx_bytes += tx_bytes; + /* tx_dropped is u32, updated without syncp protection. */ + tx_dropped += p->tx_dropped; + } + stats->tx_dropped = tx_dropped; + return stats; +} + +int mlxsw_sp_port_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid_begin, + u16 vid_end, bool is_member, bool untagged) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char *spvm_pl; + int err; + + spvm_pl = kmalloc(MLXSW_REG_SPVM_LEN, GFP_KERNEL); + if (!spvm_pl) + return -ENOMEM; + + mlxsw_reg_spvm_pack(spvm_pl, mlxsw_sp_port->local_port, vid_begin, + vid_end, is_member, untagged); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spvm), spvm_pl); + kfree(spvm_pl); + return err; +} + +static int mlxsw_sp_port_vp_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port) +{ + enum mlxsw_reg_svfa_mt mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID; + u16 vid, last_visited_vid; + int err; + + for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) { + err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, mt, true, vid, + vid); + if (err) { + last_visited_vid = vid; + goto err_port_vid_to_fid_set; + } + } + + err = mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, true); + if (err) { + last_visited_vid = VLAN_N_VID; + goto err_port_vid_to_fid_set; + } + + return 0; + +err_port_vid_to_fid_set: + for_each_set_bit(vid, mlxsw_sp_port->active_vlans, last_visited_vid) + mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, mt, false, vid, + vid); + return err; +} + +static int mlxsw_sp_port_vlan_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port) +{ + enum mlxsw_reg_svfa_mt mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID; + u16 vid; + int err; + + err = mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, false); + if (err) + return err; + + for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) { + err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, mt, false, + vid, vid); + if (err) + return err; + } + + return 0; +} + +int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto, + u16 vid) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char *sftr_pl; + int err; + + /* VLAN 0 is added to HW filter when device goes up, but it is + * reserved in our case, so simply return. + */ + if (!vid) + return 0; + + if (test_bit(vid, mlxsw_sp_port->active_vfids)) { + netdev_warn(dev, "VID=%d already configured\n", vid); + return 0; + } + + if (!test_bit(vid, mlxsw_sp->active_vfids)) { + err = mlxsw_sp_vfid_create(mlxsw_sp, vid); + if (err) { + netdev_err(dev, "Failed to create vFID=%d\n", + MLXSW_SP_VFID_BASE + vid); + return err; + } + + sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL); + if (!sftr_pl) { + err = -ENOMEM; + goto err_flood_table_alloc; + } + mlxsw_reg_sftr_pack(sftr_pl, 0, vid, + MLXSW_REG_SFGC_TABLE_TYPE_FID, 0, + MLXSW_PORT_CPU_PORT, true); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl); + kfree(sftr_pl); + if (err) { + netdev_err(dev, "Failed to configure flood table\n"); + goto err_flood_table_config; + } + } + + /* In case we fail in the following steps, we intentionally do not + * destroy the associated vFID. + */ + + /* When adding the first VLAN interface on a bridged port we need to + * transition all the active 802.1Q bridge VLANs to use explicit + * {Port, VID} to FID mappings and set the port's mode to Virtual mode. + */ + if (!mlxsw_sp_port->nr_vfids) { + err = mlxsw_sp_port_vp_mode_trans(mlxsw_sp_port); + if (err) { + netdev_err(dev, "Failed to set to Virtual mode\n"); + return err; + } + } + + err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, + MLXSW_REG_SVFA_MT_PORT_VID_TO_FID, + true, MLXSW_SP_VFID_BASE + vid, vid); + if (err) { + netdev_err(dev, "Failed to map {Port, VID=%d} to vFID=%d\n", + vid, MLXSW_SP_VFID_BASE + vid); + goto err_port_vid_to_fid_set; + } + + err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false); + if (err) { + netdev_err(dev, "Failed to disable learning for VID=%d\n", vid); + goto err_port_vid_learning_set; + } + + err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, true, false); + if (err) { + netdev_err(dev, "Failed to set VLAN membership for VID=%d\n", + vid); + goto err_port_add_vid; + } + + err = mlxsw_sp_port_stp_state_set(mlxsw_sp_port, vid, + MLXSW_REG_SPMS_STATE_FORWARDING); + if (err) { + netdev_err(dev, "Failed to set STP state for VID=%d\n", vid); + goto err_port_stp_state_set; + } + + mlxsw_sp_port->nr_vfids++; + set_bit(vid, mlxsw_sp_port->active_vfids); + + return 0; + +err_flood_table_config: +err_flood_table_alloc: + mlxsw_sp_vfid_destroy(mlxsw_sp, vid); + return err; + +err_port_stp_state_set: + mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false); +err_port_add_vid: + mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true); +err_port_vid_learning_set: + mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, + MLXSW_REG_SVFA_MT_PORT_VID_TO_FID, false, + MLXSW_SP_VFID_BASE + vid, vid); +err_port_vid_to_fid_set: + mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port); + return err; +} + +int mlxsw_sp_port_kill_vid(struct net_device *dev, + __be16 __always_unused proto, u16 vid) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + int err; + + /* VLAN 0 is removed from HW filter when device goes down, but + * it is reserved in our case, so simply return. + */ + if (!vid) + return 0; + + if (!test_bit(vid, mlxsw_sp_port->active_vfids)) { + netdev_warn(dev, "VID=%d does not exist\n", vid); + return 0; + } + + err = mlxsw_sp_port_stp_state_set(mlxsw_sp_port, vid, + MLXSW_REG_SPMS_STATE_DISCARDING); + if (err) { + netdev_err(dev, "Failed to set STP state for VID=%d\n", vid); + return err; + } + + err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false); + if (err) { + netdev_err(dev, "Failed to set VLAN membership for VID=%d\n", + vid); + return err; + } + + err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true); + if (err) { + netdev_err(dev, "Failed to enable learning for VID=%d\n", vid); + return err; + } + + err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, + MLXSW_REG_SVFA_MT_PORT_VID_TO_FID, + false, MLXSW_SP_VFID_BASE + vid, + vid); + if (err) { + netdev_err(dev, "Failed to invalidate {Port, VID=%d} to vFID=%d mapping\n", + vid, MLXSW_SP_VFID_BASE + vid); + return err; + } + + /* When removing the last VLAN interface on a bridged port we need to + * transition all active 802.1Q bridge VLANs to use VID to FID + * mappings and set port's mode to VLAN mode. + */ + if (mlxsw_sp_port->nr_vfids == 1) { + err = mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port); + if (err) { + netdev_err(dev, "Failed to set to VLAN mode\n"); + return err; + } + } + + mlxsw_sp_port->nr_vfids--; + clear_bit(vid, mlxsw_sp_port->active_vfids); + + return 0; +} + +static const struct net_device_ops mlxsw_sp_port_netdev_ops = { + .ndo_open = mlxsw_sp_port_open, + .ndo_stop = mlxsw_sp_port_stop, + .ndo_start_xmit = mlxsw_sp_port_xmit, + .ndo_set_mac_address = mlxsw_sp_port_set_mac_address, + .ndo_change_mtu = mlxsw_sp_port_change_mtu, + .ndo_get_stats64 = mlxsw_sp_port_get_stats64, + .ndo_vlan_rx_add_vid = mlxsw_sp_port_add_vid, + .ndo_vlan_rx_kill_vid = mlxsw_sp_port_kill_vid, + .ndo_fdb_add = switchdev_port_fdb_add, + .ndo_fdb_del = switchdev_port_fdb_del, + .ndo_fdb_dump = switchdev_port_fdb_dump, + .ndo_bridge_setlink = switchdev_port_bridge_setlink, + .ndo_bridge_getlink = switchdev_port_bridge_getlink, + .ndo_bridge_dellink = switchdev_port_bridge_dellink, +}; + +static void mlxsw_sp_port_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *drvinfo) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + + strlcpy(drvinfo->driver, mlxsw_sp_driver_name, sizeof(drvinfo->driver)); + strlcpy(drvinfo->version, mlxsw_sp_driver_version, + sizeof(drvinfo->version)); + snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), + "%d.%d.%d", + mlxsw_sp->bus_info->fw_rev.major, + mlxsw_sp->bus_info->fw_rev.minor, + mlxsw_sp->bus_info->fw_rev.subminor); + strlcpy(drvinfo->bus_info, mlxsw_sp->bus_info->device_name, + sizeof(drvinfo->bus_info)); +} + +struct mlxsw_sp_port_hw_stats { + char str[ETH_GSTRING_LEN]; + u64 (*getter)(char *payload); +}; + +static const struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_stats[] = { + { + .str = "a_frames_transmitted_ok", + .getter = mlxsw_reg_ppcnt_a_frames_transmitted_ok_get, + }, + { + .str = "a_frames_received_ok", + .getter = mlxsw_reg_ppcnt_a_frames_received_ok_get, + }, + { + .str = "a_frame_check_sequence_errors", + .getter = mlxsw_reg_ppcnt_a_frame_check_sequence_errors_get, + }, + { + .str = "a_alignment_errors", + .getter = mlxsw_reg_ppcnt_a_alignment_errors_get, + }, + { + .str = "a_octets_transmitted_ok", + .getter = mlxsw_reg_ppcnt_a_octets_transmitted_ok_get, + }, + { + .str = "a_octets_received_ok", + .getter = mlxsw_reg_ppcnt_a_octets_received_ok_get, + }, + { + .str = "a_multicast_frames_xmitted_ok", + .getter = mlxsw_reg_ppcnt_a_multicast_frames_xmitted_ok_get, + }, + { + .str = "a_broadcast_frames_xmitted_ok", + .getter = mlxsw_reg_ppcnt_a_broadcast_frames_xmitted_ok_get, + }, + { + .str = "a_multicast_frames_received_ok", + .getter = mlxsw_reg_ppcnt_a_multicast_frames_received_ok_get, + }, + { + .str = "a_broadcast_frames_received_ok", + .getter = mlxsw_reg_ppcnt_a_broadcast_frames_received_ok_get, + }, + { + .str = "a_in_range_length_errors", + .getter = mlxsw_reg_ppcnt_a_in_range_length_errors_get, + }, + { + .str = "a_out_of_range_length_field", + .getter = mlxsw_reg_ppcnt_a_out_of_range_length_field_get, + }, + { + .str = "a_frame_too_long_errors", + .getter = mlxsw_reg_ppcnt_a_frame_too_long_errors_get, + }, + { + .str = "a_symbol_error_during_carrier", + .getter = mlxsw_reg_ppcnt_a_symbol_error_during_carrier_get, + }, + { + .str = "a_mac_control_frames_transmitted", + .getter = mlxsw_reg_ppcnt_a_mac_control_frames_transmitted_get, + }, + { + .str = "a_mac_control_frames_received", + .getter = mlxsw_reg_ppcnt_a_mac_control_frames_received_get, + }, + { + .str = "a_unsupported_opcodes_received", + .getter = mlxsw_reg_ppcnt_a_unsupported_opcodes_received_get, + }, + { + .str = "a_pause_mac_ctrl_frames_received", + .getter = mlxsw_reg_ppcnt_a_pause_mac_ctrl_frames_received_get, + }, + { + .str = "a_pause_mac_ctrl_frames_xmitted", + .getter = mlxsw_reg_ppcnt_a_pause_mac_ctrl_frames_transmitted_get, + }, +}; + +#define MLXSW_SP_PORT_HW_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_stats) + +static void mlxsw_sp_port_get_strings(struct net_device *dev, + u32 stringset, u8 *data) +{ + u8 *p = data; + int i; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < MLXSW_SP_PORT_HW_STATS_LEN; i++) { + memcpy(p, mlxsw_sp_port_hw_stats[i].str, + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + break; + } +} + +static void mlxsw_sp_port_get_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char ppcnt_pl[MLXSW_REG_PPCNT_LEN]; + int i; + int err; + + mlxsw_reg_ppcnt_pack(ppcnt_pl, mlxsw_sp_port->local_port); + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ppcnt), ppcnt_pl); + for (i = 0; i < MLXSW_SP_PORT_HW_STATS_LEN; i++) + data[i] = !err ? mlxsw_sp_port_hw_stats[i].getter(ppcnt_pl) : 0; +} + +static int mlxsw_sp_port_get_sset_count(struct net_device *dev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return MLXSW_SP_PORT_HW_STATS_LEN; + default: + return -EOPNOTSUPP; + } +} + +struct mlxsw_sp_port_link_mode { + u32 mask; + u32 supported; + u32 advertised; + u32 speed; +}; + +static const struct mlxsw_sp_port_link_mode mlxsw_sp_port_link_mode[] = { + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_100BASE_T, + .supported = SUPPORTED_100baseT_Full, + .advertised = ADVERTISED_100baseT_Full, + .speed = 100, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_100BASE_TX, + .speed = 100, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_SGMII | + MLXSW_REG_PTYS_ETH_SPEED_1000BASE_KX, + .supported = SUPPORTED_1000baseKX_Full, + .advertised = ADVERTISED_1000baseKX_Full, + .speed = 1000, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_T, + .supported = SUPPORTED_10000baseT_Full, + .advertised = ADVERTISED_10000baseT_Full, + .speed = 10000, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CX4 | + MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4, + .supported = SUPPORTED_10000baseKX4_Full, + .advertised = ADVERTISED_10000baseKX4_Full, + .speed = 10000, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR | + MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR | + MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR | + MLXSW_REG_PTYS_ETH_SPEED_10GBASE_ER_LR, + .supported = SUPPORTED_10000baseKR_Full, + .advertised = ADVERTISED_10000baseKR_Full, + .speed = 10000, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_20GBASE_KR2, + .supported = SUPPORTED_20000baseKR2_Full, + .advertised = ADVERTISED_20000baseKR2_Full, + .speed = 20000, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4, + .supported = SUPPORTED_40000baseCR4_Full, + .advertised = ADVERTISED_40000baseCR4_Full, + .speed = 40000, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4, + .supported = SUPPORTED_40000baseKR4_Full, + .advertised = ADVERTISED_40000baseKR4_Full, + .speed = 40000, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4, + .supported = SUPPORTED_40000baseSR4_Full, + .advertised = ADVERTISED_40000baseSR4_Full, + .speed = 40000, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_LR4_ER4, + .supported = SUPPORTED_40000baseLR4_Full, + .advertised = ADVERTISED_40000baseLR4_Full, + .speed = 40000, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_25GBASE_CR | + MLXSW_REG_PTYS_ETH_SPEED_25GBASE_KR | + MLXSW_REG_PTYS_ETH_SPEED_25GBASE_SR, + .speed = 25000, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_50GBASE_KR4 | + MLXSW_REG_PTYS_ETH_SPEED_50GBASE_CR2 | + MLXSW_REG_PTYS_ETH_SPEED_50GBASE_KR2, + .speed = 50000, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_56GBASE_R4, + .supported = SUPPORTED_56000baseKR4_Full, + .advertised = ADVERTISED_56000baseKR4_Full, + .speed = 56000, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_100GBASE_CR4 | + MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4 | + MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4 | + MLXSW_REG_PTYS_ETH_SPEED_100GBASE_LR4_ER4, + .speed = 100000, + }, +}; + +#define MLXSW_SP_PORT_LINK_MODE_LEN ARRAY_SIZE(mlxsw_sp_port_link_mode) + +static u32 mlxsw_sp_from_ptys_supported_port(u32 ptys_eth_proto) +{ + if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR | + MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR | + MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4 | + MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4 | + MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4 | + MLXSW_REG_PTYS_ETH_SPEED_SGMII)) + return SUPPORTED_FIBRE; + + if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR | + MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4 | + MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4 | + MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4 | + MLXSW_REG_PTYS_ETH_SPEED_1000BASE_KX)) + return SUPPORTED_Backplane; + return 0; +} + +static u32 mlxsw_sp_from_ptys_supported_link(u32 ptys_eth_proto) +{ + u32 modes = 0; + int i; + + for (i = 0; i < MLXSW_SP_PORT_LINK_MODE_LEN; i++) { + if (ptys_eth_proto & mlxsw_sp_port_link_mode[i].mask) + modes |= mlxsw_sp_port_link_mode[i].supported; + } + return modes; +} + +static u32 mlxsw_sp_from_ptys_advert_link(u32 ptys_eth_proto) +{ + u32 modes = 0; + int i; + + for (i = 0; i < MLXSW_SP_PORT_LINK_MODE_LEN; i++) { + if (ptys_eth_proto & mlxsw_sp_port_link_mode[i].mask) + modes |= mlxsw_sp_port_link_mode[i].advertised; + } + return modes; +} + +static void mlxsw_sp_from_ptys_speed_duplex(bool carrier_ok, u32 ptys_eth_proto, + struct ethtool_cmd *cmd) +{ + u32 speed = SPEED_UNKNOWN; + u8 duplex = DUPLEX_UNKNOWN; + int i; + + if (!carrier_ok) + goto out; + + for (i = 0; i < MLXSW_SP_PORT_LINK_MODE_LEN; i++) { + if (ptys_eth_proto & mlxsw_sp_port_link_mode[i].mask) { + speed = mlxsw_sp_port_link_mode[i].speed; + duplex = DUPLEX_FULL; + break; + } + } +out: + ethtool_cmd_speed_set(cmd, speed); + cmd->duplex = duplex; +} + +static u8 mlxsw_sp_port_connector_port(u32 ptys_eth_proto) +{ + if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR | + MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4 | + MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4 | + MLXSW_REG_PTYS_ETH_SPEED_SGMII)) + return PORT_FIBRE; + + if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR | + MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4 | + MLXSW_REG_PTYS_ETH_SPEED_100GBASE_CR4)) + return PORT_DA; + + if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR | + MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4 | + MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4 | + MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4)) + return PORT_NONE; + + return PORT_OTHER; +} + +static int mlxsw_sp_port_get_settings(struct net_device *dev, + struct ethtool_cmd *cmd) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char ptys_pl[MLXSW_REG_PTYS_LEN]; + u32 eth_proto_cap; + u32 eth_proto_admin; + u32 eth_proto_oper; + int err; + + mlxsw_reg_ptys_pack(ptys_pl, mlxsw_sp_port->local_port, 0); + err = mlxsw_reg_query(mlxsw_sp->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); + + cmd->supported = mlxsw_sp_from_ptys_supported_port(eth_proto_cap) | + mlxsw_sp_from_ptys_supported_link(eth_proto_cap) | + SUPPORTED_Pause | SUPPORTED_Asym_Pause; + cmd->advertising = mlxsw_sp_from_ptys_advert_link(eth_proto_admin); + mlxsw_sp_from_ptys_speed_duplex(netif_carrier_ok(dev), + eth_proto_oper, cmd); + + eth_proto_oper = eth_proto_oper ? eth_proto_oper : eth_proto_cap; + cmd->port = mlxsw_sp_port_connector_port(eth_proto_oper); + cmd->lp_advertising = mlxsw_sp_from_ptys_advert_link(eth_proto_oper); + + cmd->transceiver = XCVR_INTERNAL; + return 0; +} + +static u32 mlxsw_sp_to_ptys_advert_link(u32 advertising) +{ + u32 ptys_proto = 0; + int i; + + for (i = 0; i < MLXSW_SP_PORT_LINK_MODE_LEN; i++) { + if (advertising & mlxsw_sp_port_link_mode[i].advertised) + ptys_proto |= mlxsw_sp_port_link_mode[i].mask; + } + return ptys_proto; +} + +static u32 mlxsw_sp_to_ptys_speed(u32 speed) +{ + u32 ptys_proto = 0; + int i; + + for (i = 0; i < MLXSW_SP_PORT_LINK_MODE_LEN; i++) { + if (speed == mlxsw_sp_port_link_mode[i].speed) + ptys_proto |= mlxsw_sp_port_link_mode[i].mask; + } + return ptys_proto; +} + +static int mlxsw_sp_port_set_settings(struct net_device *dev, + struct ethtool_cmd *cmd) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char ptys_pl[MLXSW_REG_PTYS_LEN]; + u32 speed; + u32 eth_proto_new; + u32 eth_proto_cap; + u32 eth_proto_admin; + bool is_up; + int err; + + speed = ethtool_cmd_speed(cmd); + + eth_proto_new = cmd->autoneg == AUTONEG_ENABLE ? + mlxsw_sp_to_ptys_advert_link(cmd->advertising) : + mlxsw_sp_to_ptys_speed(speed); + + mlxsw_reg_ptys_pack(ptys_pl, mlxsw_sp_port->local_port, 0); + err = mlxsw_reg_query(mlxsw_sp->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); + + eth_proto_new = eth_proto_new & eth_proto_cap; + if (!eth_proto_new) { + netdev_err(dev, "Not supported proto admin requested"); + return -EINVAL; + } + if (eth_proto_new == eth_proto_admin) + return 0; + + mlxsw_reg_ptys_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) { + netdev_err(dev, "Failed to set proto admin"); + return err; + } + + err = mlxsw_sp_port_oper_status_get(mlxsw_sp_port, &is_up); + if (err) { + netdev_err(dev, "Failed to get oper status"); + return err; + } + if (!is_up) + return 0; + + err = mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false); + if (err) { + netdev_err(dev, "Failed to set admin status"); + return err; + } + + err = mlxsw_sp_port_admin_status_set(mlxsw_sp_port, true); + if (err) { + netdev_err(dev, "Failed to set admin status"); + return err; + } + + return 0; +} + +static const struct ethtool_ops mlxsw_sp_port_ethtool_ops = { + .get_drvinfo = mlxsw_sp_port_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_strings = mlxsw_sp_port_get_strings, + .get_ethtool_stats = mlxsw_sp_port_get_stats, + .get_sset_count = mlxsw_sp_port_get_sset_count, + .get_settings = mlxsw_sp_port_get_settings, + .set_settings = mlxsw_sp_port_set_settings, +}; + +static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port) +{ + struct mlxsw_sp_port *mlxsw_sp_port; + struct net_device *dev; + bool usable; + int err; + + dev = alloc_etherdev(sizeof(struct mlxsw_sp_port)); + if (!dev) + return -ENOMEM; + mlxsw_sp_port = netdev_priv(dev); + mlxsw_sp_port->dev = dev; + mlxsw_sp_port->mlxsw_sp = mlxsw_sp; + mlxsw_sp_port->local_port = local_port; + mlxsw_sp_port->learning = 1; + mlxsw_sp_port->learning_sync = 1; + mlxsw_sp_port->pvid = 1; + + mlxsw_sp_port->pcpu_stats = + netdev_alloc_pcpu_stats(struct mlxsw_sp_port_pcpu_stats); + if (!mlxsw_sp_port->pcpu_stats) { + err = -ENOMEM; + goto err_alloc_stats; + } + + dev->netdev_ops = &mlxsw_sp_port_netdev_ops; + dev->ethtool_ops = &mlxsw_sp_port_ethtool_ops; + + err = mlxsw_sp_port_dev_addr_init(mlxsw_sp_port); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Port %d: Unable to init port mac address\n", + mlxsw_sp_port->local_port); + goto err_dev_addr_init; + } + + netif_carrier_off(dev); + + dev->features |= NETIF_F_NETNS_LOCAL | NETIF_F_LLTX | NETIF_F_SG | + NETIF_F_HW_VLAN_CTAG_FILTER; + + /* Each packet needs to have a Tx header (metadata) on top all other + * headers. + */ + dev->hard_header_len += MLXSW_TXHDR_LEN; + + err = mlxsw_sp_port_module_check(mlxsw_sp_port, &usable); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to check module\n", + mlxsw_sp_port->local_port); + goto err_port_module_check; + } + + if (!usable) { + dev_dbg(mlxsw_sp->bus_info->dev, "Port %d: Not usable, skipping initialization\n", + mlxsw_sp_port->local_port); + goto port_not_usable; + } + + err = mlxsw_sp_port_system_port_mapping_set(mlxsw_sp_port); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set system port mapping\n", + mlxsw_sp_port->local_port); + goto err_port_system_port_mapping_set; + } + + err = mlxsw_sp_port_swid_set(mlxsw_sp_port, 0); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set SWID\n", + mlxsw_sp_port->local_port); + goto err_port_swid_set; + } + + err = mlxsw_sp_port_mtu_set(mlxsw_sp_port, ETH_DATA_LEN); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set MTU\n", + mlxsw_sp_port->local_port); + goto err_port_mtu_set; + } + + err = mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false); + if (err) + goto err_port_admin_status_set; + + err = mlxsw_sp_port_buffers_init(mlxsw_sp_port); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to initialize buffers\n", + mlxsw_sp_port->local_port); + goto err_port_buffers_init; + } + + mlxsw_sp_port_switchdev_init(mlxsw_sp_port); + err = register_netdev(dev); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to register netdev\n", + mlxsw_sp_port->local_port); + goto err_register_netdev; + } + + err = mlxsw_sp_port_vlan_init(mlxsw_sp_port); + if (err) + goto err_port_vlan_init; + + mlxsw_sp->ports[local_port] = mlxsw_sp_port; + return 0; + +err_port_vlan_init: + unregister_netdev(dev); +err_register_netdev: +err_port_buffers_init: +err_port_admin_status_set: +err_port_mtu_set: +err_port_swid_set: +err_port_system_port_mapping_set: +port_not_usable: +err_port_module_check: +err_dev_addr_init: + free_percpu(mlxsw_sp_port->pcpu_stats); +err_alloc_stats: + free_netdev(dev); + return err; +} + +static void mlxsw_sp_vfids_fini(struct mlxsw_sp *mlxsw_sp) +{ + u16 vfid; + + for_each_set_bit(vfid, mlxsw_sp->active_vfids, VLAN_N_VID) + mlxsw_sp_vfid_destroy(mlxsw_sp, vfid); +} + +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; + mlxsw_sp_port_kill_vid(mlxsw_sp_port->dev, 0, 1); + unregister_netdev(mlxsw_sp_port->dev); /* This calls ndo_stop */ + mlxsw_sp_port_switchdev_fini(mlxsw_sp_port); + free_percpu(mlxsw_sp_port->pcpu_stats); + free_netdev(mlxsw_sp_port->dev); +} + +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); + kfree(mlxsw_sp->ports); +} + +static int mlxsw_sp_ports_create(struct mlxsw_sp *mlxsw_sp) +{ + size_t alloc_size; + int i; + int err; + + alloc_size = sizeof(struct mlxsw_sp_port *) * MLXSW_PORT_MAX_PORTS; + mlxsw_sp->ports = kzalloc(alloc_size, GFP_KERNEL); + if (!mlxsw_sp->ports) + return -ENOMEM; + + for (i = 1; i < MLXSW_PORT_MAX_PORTS; i++) { + err = mlxsw_sp_port_create(mlxsw_sp, i); + if (err) + goto err_port_create; + } + return 0; + +err_port_create: + for (i--; i >= 1; i--) + mlxsw_sp_port_remove(mlxsw_sp, i); + kfree(mlxsw_sp->ports); + return err; +} + +static void mlxsw_sp_pude_event_func(const struct mlxsw_reg_info *reg, + char *pude_pl, void *priv) +{ + struct mlxsw_sp *mlxsw_sp = priv; + struct mlxsw_sp_port *mlxsw_sp_port; + enum mlxsw_reg_pude_oper_status status; + u8 local_port; + + local_port = mlxsw_reg_pude_local_port_get(pude_pl); + mlxsw_sp_port = mlxsw_sp->ports[local_port]; + if (!mlxsw_sp_port) { + dev_warn(mlxsw_sp->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); + if (status == MLXSW_PORT_OPER_STATUS_UP) { + netdev_info(mlxsw_sp_port->dev, "link up\n"); + netif_carrier_on(mlxsw_sp_port->dev); + } else { + netdev_info(mlxsw_sp_port->dev, "link down\n"); + netif_carrier_off(mlxsw_sp_port->dev); + } +} + +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_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]; + struct mlxsw_sp_port_pcpu_stats *pcpu_stats; + + if (unlikely(!mlxsw_sp_port)) { + dev_warn_ratelimited(mlxsw_sp->bus_info->dev, "Port %d: skb received for non-existent port\n", + local_port); + return; + } + + skb->dev = mlxsw_sp_port->dev; + + pcpu_stats = this_cpu_ptr(mlxsw_sp_port->pcpu_stats); + u64_stats_update_begin(&pcpu_stats->syncp); + pcpu_stats->rx_packets++; + pcpu_stats->rx_bytes += skb->len; + u64_stats_update_end(&pcpu_stats->syncp); + + skb->protocol = eth_type_trans(skb, skb->dev); + netif_receive_skb(skb); +} + +static const struct mlxsw_rx_listener mlxsw_sp_rx_listener[] = { + { + .func = mlxsw_sp_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_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_STP, + }, + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_LACP, + }, + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_EAPOL, + }, + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_LLDP, + }, + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_MMRP, + }, + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_MVRP, + }, + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_RPVST, + }, + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_DHCP, + }, + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_IGMP_QUERY, + }, + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_IGMP_V1_REPORT, + }, + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_IGMP_V2_REPORT, + }, + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_IGMP_V2_LEAVE, + }, + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_IGMP_V3_REPORT, + }, +}; + +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; + + 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); + if (err) + 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); + if (err) + goto err_rx_listener_register; + + mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_TRAP_TO_CPU, + 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_FORWARD, + 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); + } + 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_FORWARD, + 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); + } +} + +static int __mlxsw_sp_flood_init(struct mlxsw_core *mlxsw_core, + enum mlxsw_reg_sfgc_type type, + enum mlxsw_reg_sfgc_bridge_type bridge_type) +{ + enum mlxsw_flood_table_type table_type; + enum mlxsw_sp_flood_table flood_table; + char sfgc_pl[MLXSW_REG_SFGC_LEN]; + + if (bridge_type == MLXSW_REG_SFGC_BRIDGE_TYPE_VFID) { + table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID; + flood_table = 0; + } else { + table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST; + if (type == MLXSW_REG_SFGC_TYPE_UNKNOWN_UNICAST) + flood_table = MLXSW_SP_FLOOD_TABLE_UC; + else + flood_table = MLXSW_SP_FLOOD_TABLE_BM; + } + + mlxsw_reg_sfgc_pack(sfgc_pl, type, bridge_type, table_type, + flood_table); + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(sfgc), sfgc_pl); +} + +static int mlxsw_sp_flood_init(struct mlxsw_sp *mlxsw_sp) +{ + int type, err; + + /* For non-offloaded netdevs, flood all traffic types to CPU + * port. + */ + for (type = 0; type < MLXSW_REG_SFGC_TYPE_MAX; type++) { + if (type == MLXSW_REG_SFGC_TYPE_RESERVED) + continue; + + err = __mlxsw_sp_flood_init(mlxsw_sp->core, type, + MLXSW_REG_SFGC_BRIDGE_TYPE_VFID); + if (err) + return err; + } + + /* For bridged ports, use one flooding table for unknown unicast + * traffic and a second table for unregistered multicast and + * broadcast. + */ + for (type = 0; type < MLXSW_REG_SFGC_TYPE_MAX; type++) { + if (type == MLXSW_REG_SFGC_TYPE_RESERVED) + continue; + + err = __mlxsw_sp_flood_init(mlxsw_sp->core, type, + MLXSW_REG_SFGC_BRIDGE_TYPE_1Q_FID); + if (err) + return err; + } + + return 0; +} + +static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core, + const struct mlxsw_bus_info *mlxsw_bus_info) +{ + struct mlxsw_sp *mlxsw_sp = priv; + int err; + + mlxsw_sp->core = mlxsw_core; + mlxsw_sp->bus_info = mlxsw_bus_info; + + err = mlxsw_sp_base_mac_get(mlxsw_sp); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Failed to get base mac\n"); + return err; + } + + err = mlxsw_sp_ports_create(mlxsw_sp); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Failed to create ports\n"); + goto err_ports_create; + } + + 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"); + goto err_event_register; + } + + 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; + } + + err = mlxsw_sp_flood_init(mlxsw_sp); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize flood tables\n"); + goto err_flood_init; + } + + err = mlxsw_sp_buffers_init(mlxsw_sp); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize buffers\n"); + goto err_buffers_init; + } + + err = mlxsw_sp_switchdev_init(mlxsw_sp); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize switchdev\n"); + goto err_switchdev_init; + } + + return 0; + +err_switchdev_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); +err_event_register: + mlxsw_sp_ports_remove(mlxsw_sp); +err_ports_create: + mlxsw_sp_vfids_fini(mlxsw_sp); + return err; +} + +static void mlxsw_sp_fini(void *priv) +{ + struct mlxsw_sp *mlxsw_sp = priv; + + mlxsw_sp_switchdev_fini(mlxsw_sp); + mlxsw_sp_traps_fini(mlxsw_sp); + mlxsw_sp_event_unregister(mlxsw_sp, MLXSW_TRAP_ID_PUDE); + mlxsw_sp_ports_remove(mlxsw_sp); + mlxsw_sp_vfids_fini(mlxsw_sp); +} + +static struct mlxsw_config_profile mlxsw_sp_config_profile = { + .used_max_vepa_channels = 1, + .max_vepa_channels = 0, + .used_max_lag = 1, + .max_lag = 64, + .used_max_port_per_lag = 1, + .max_port_per_lag = 16, + .used_max_mid = 1, + .max_mid = 7000, + .used_max_pgt = 1, + .max_pgt = 0, + .used_max_system_port = 1, + .max_system_port = 64, + .used_max_vlan_groups = 1, + .max_vlan_groups = 127, + .used_max_regions = 1, + .max_regions = 400, + .used_flood_tables = 1, + .used_flood_mode = 1, + .flood_mode = 3, + .max_fid_offset_flood_tables = 2, + .fid_offset_flood_table_size = VLAN_N_VID - 1, + .max_fid_flood_tables = 1, + .fid_flood_table_size = VLAN_N_VID, + .used_max_ib_mc = 1, + .max_ib_mc = 0, + .used_max_pkey = 1, + .max_pkey = 0, + .swid_config = { + { + .used_type = 1, + .type = MLXSW_PORT_SWID_TYPE_ETH, + } + }, +}; + +static struct mlxsw_driver mlxsw_sp_driver = { + .kind = MLXSW_DEVICE_KIND_SPECTRUM, + .owner = THIS_MODULE, + .priv_size = sizeof(struct mlxsw_sp), + .init = mlxsw_sp_init, + .fini = mlxsw_sp_fini, + .txhdr_construct = mlxsw_sp_txhdr_construct, + .txhdr_len = MLXSW_TXHDR_LEN, + .profile = &mlxsw_sp_config_profile, +}; + +static bool mlxsw_sp_port_dev_check(const struct net_device *dev) +{ + return dev->netdev_ops == &mlxsw_sp_port_netdev_ops; +} + +static int mlxsw_sp_port_bridge_join(struct mlxsw_sp_port *mlxsw_sp_port) +{ + struct net_device *dev = mlxsw_sp_port->dev; + int err; + + /* When port is not bridged untagged packets are tagged with + * PVID=VID=1, thereby creating an implicit VLAN interface in + * the device. Remove it and let bridge code take care of its + * own VLANs. + */ + err = mlxsw_sp_port_kill_vid(dev, 0, 1); + if (err) + netdev_err(dev, "Failed to remove VID 1\n"); + + return err; +} + +static int mlxsw_sp_port_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_port) +{ + struct net_device *dev = mlxsw_sp_port->dev; + int err; + + /* Add implicit VLAN interface in the device, so that untagged + * packets will be classified to the default vFID. + */ + err = mlxsw_sp_port_add_vid(dev, 0, 1); + if (err) + netdev_err(dev, "Failed to add VID 1\n"); + + return err; +} + +static bool mlxsw_sp_master_bridge_check(struct mlxsw_sp *mlxsw_sp, + struct net_device *br_dev) +{ + return !mlxsw_sp->master_bridge.dev || + mlxsw_sp->master_bridge.dev == br_dev; +} + +static void mlxsw_sp_master_bridge_inc(struct mlxsw_sp *mlxsw_sp, + struct net_device *br_dev) +{ + mlxsw_sp->master_bridge.dev = br_dev; + mlxsw_sp->master_bridge.ref_count++; +} + +static void mlxsw_sp_master_bridge_dec(struct mlxsw_sp *mlxsw_sp, + struct net_device *br_dev) +{ + if (--mlxsw_sp->master_bridge.ref_count == 0) + mlxsw_sp->master_bridge.dev = NULL; +} + +static int mlxsw_sp_netdevice_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct netdev_notifier_changeupper_info *info; + struct mlxsw_sp_port *mlxsw_sp_port; + struct net_device *upper_dev; + struct mlxsw_sp *mlxsw_sp; + int err; + + if (!mlxsw_sp_port_dev_check(dev)) + return NOTIFY_DONE; + + mlxsw_sp_port = netdev_priv(dev); + mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + info = ptr; + + switch (event) { + case NETDEV_PRECHANGEUPPER: + upper_dev = info->upper_dev; + /* HW limitation forbids to put ports to multiple bridges. */ + if (info->master && info->linking && + netif_is_bridge_master(upper_dev) && + !mlxsw_sp_master_bridge_check(mlxsw_sp, upper_dev)) + return NOTIFY_BAD; + break; + case NETDEV_CHANGEUPPER: + upper_dev = info->upper_dev; + if (info->master && + netif_is_bridge_master(upper_dev)) { + if (info->linking) { + err = mlxsw_sp_port_bridge_join(mlxsw_sp_port); + if (err) + netdev_err(dev, "Failed to join bridge\n"); + mlxsw_sp_master_bridge_inc(mlxsw_sp, upper_dev); + mlxsw_sp_port->bridged = true; + } else { + err = mlxsw_sp_port_bridge_leave(mlxsw_sp_port); + if (err) + netdev_err(dev, "Failed to leave bridge\n"); + mlxsw_sp_port->bridged = false; + mlxsw_sp_master_bridge_dec(mlxsw_sp, upper_dev); + } + } + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block mlxsw_sp_netdevice_nb __read_mostly = { + .notifier_call = mlxsw_sp_netdevice_event, +}; + +static int __init mlxsw_sp_module_init(void) +{ + int err; + + register_netdevice_notifier(&mlxsw_sp_netdevice_nb); + err = mlxsw_core_driver_register(&mlxsw_sp_driver); + if (err) + goto err_core_driver_register; + return 0; + +err_core_driver_register: + unregister_netdevice_notifier(&mlxsw_sp_netdevice_nb); + return err; +} + +static void __exit mlxsw_sp_module_exit(void) +{ + mlxsw_core_driver_unregister(&mlxsw_sp_driver); + unregister_netdevice_notifier(&mlxsw_sp_netdevice_nb); +} + +module_init(mlxsw_sp_module_init); +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); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h new file mode 100644 index 0000000..fc00749 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -0,0 +1,121 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/spectrum.h + * Copyright (c) 2015 Mellanox Technologies. All rights reserved. + * Copyright (c) 2015 Jiri Pirko + * Copyright (c) 2015 Ido Schimmel + * Copyright (c) 2015 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_SPECTRUM_H +#define _MLXSW_SPECTRUM_H + +#include +#include +#include +#include +#include + +#include "core.h" + +#define MLXSW_SP_VFID_BASE VLAN_N_VID + +struct mlxsw_sp_port; + +struct mlxsw_sp { + unsigned long active_vfids[BITS_TO_LONGS(VLAN_N_VID)]; + unsigned long active_fids[BITS_TO_LONGS(VLAN_N_VID)]; + struct mlxsw_sp_port **ports; + struct mlxsw_core *core; + const struct mlxsw_bus_info *bus_info; + unsigned char base_mac[ETH_ALEN]; + struct { + struct delayed_work dw; +#define MLXSW_SP_DEFAULT_LEARNING_INTERVAL 100 + unsigned int interval; /* ms */ + } fdb_notify; +#define MLXSW_SP_DEFAULT_AGEING_TIME 300 + u32 ageing_time; + struct { + struct net_device *dev; + unsigned int ref_count; + } master_bridge; +}; + +struct mlxsw_sp_port_pcpu_stats { + u64 rx_packets; + u64 rx_bytes; + u64 tx_packets; + u64 tx_bytes; + struct u64_stats_sync syncp; + u32 tx_dropped; +}; + +struct mlxsw_sp_port { + struct net_device *dev; + struct mlxsw_sp_port_pcpu_stats __percpu *pcpu_stats; + struct mlxsw_sp *mlxsw_sp; + u8 local_port; + u8 stp_state; + u8 learning:1; + u8 learning_sync:1; + u16 pvid; + bool bridged; + /* 802.1Q bridge VLANs */ + unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; + /* VLAN interfaces */ + unsigned long active_vfids[BITS_TO_LONGS(VLAN_N_VID)]; + u16 nr_vfids; +}; + +enum mlxsw_sp_flood_table { + MLXSW_SP_FLOOD_TABLE_UC, + MLXSW_SP_FLOOD_TABLE_BM, +}; + +int mlxsw_sp_buffers_init(struct mlxsw_sp *mlxsw_sp); +int mlxsw_sp_port_buffers_init(struct mlxsw_sp_port *mlxsw_sp_port); + +int mlxsw_sp_switchdev_init(struct mlxsw_sp *mlxsw_sp); +void mlxsw_sp_switchdev_fini(struct mlxsw_sp *mlxsw_sp); +int mlxsw_sp_port_vlan_init(struct mlxsw_sp_port *mlxsw_sp_port); +void mlxsw_sp_port_switchdev_init(struct mlxsw_sp_port *mlxsw_sp_port); +void mlxsw_sp_port_switchdev_fini(struct mlxsw_sp_port *mlxsw_sp_port); +int mlxsw_sp_port_vid_to_fid_set(struct mlxsw_sp_port *mlxsw_sp_port, + enum mlxsw_reg_svfa_mt mt, bool valid, u16 fid, + u16 vid); +int mlxsw_sp_port_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid_begin, + u16 vid_end, bool is_member, bool untagged); +int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto, + u16 vid); +int mlxsw_sp_port_kill_vid(struct net_device *dev, + __be16 __always_unused proto, u16 vid); + +#endif diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c new file mode 100644 index 0000000..d59195e --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c @@ -0,0 +1,422 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c + * 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. + */ + +#include +#include + +#include "spectrum.h" +#include "core.h" +#include "port.h" +#include "reg.h" + +struct mlxsw_sp_pb { + u8 index; + u16 size; +}; + +#define MLXSW_SP_PB(_index, _size) \ + { \ + .index = _index, \ + .size = _size, \ + } + +static const struct mlxsw_sp_pb mlxsw_sp_pbs[] = { + MLXSW_SP_PB(0, 208), + MLXSW_SP_PB(1, 208), + MLXSW_SP_PB(2, 208), + MLXSW_SP_PB(3, 208), + MLXSW_SP_PB(4, 208), + MLXSW_SP_PB(5, 208), + MLXSW_SP_PB(6, 208), + MLXSW_SP_PB(7, 208), + MLXSW_SP_PB(9, 208), +}; + +#define MLXSW_SP_PBS_LEN ARRAY_SIZE(mlxsw_sp_pbs) + +static int mlxsw_sp_port_pb_init(struct mlxsw_sp_port *mlxsw_sp_port) +{ + char pbmc_pl[MLXSW_REG_PBMC_LEN]; + int i; + + mlxsw_reg_pbmc_pack(pbmc_pl, mlxsw_sp_port->local_port, + 0xffff, 0xffff / 2); + for (i = 0; i < MLXSW_SP_PBS_LEN; i++) { + const struct mlxsw_sp_pb *pb; + + pb = &mlxsw_sp_pbs[i]; + mlxsw_reg_pbmc_lossy_buffer_pack(pbmc_pl, pb->index, pb->size); + } + return mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, + MLXSW_REG(pbmc), pbmc_pl); +} + +#define MLXSW_SP_SB_BYTES_PER_CELL 96 + +struct mlxsw_sp_sb_pool { + u8 pool; + enum mlxsw_reg_sbpr_dir dir; + enum mlxsw_reg_sbpr_mode mode; + u32 size; +}; + +#define MLXSW_SP_SB_POOL_INGRESS_SIZE \ + ((15000000 - (2 * 20000 * MLXSW_PORT_MAX_PORTS)) / \ + MLXSW_SP_SB_BYTES_PER_CELL) +#define MLXSW_SP_SB_POOL_EGRESS_SIZE \ + ((14000000 - (8 * 1500 * MLXSW_PORT_MAX_PORTS)) / \ + MLXSW_SP_SB_BYTES_PER_CELL) + +#define MLXSW_SP_SB_POOL(_pool, _dir, _mode, _size) \ + { \ + .pool = _pool, \ + .dir = _dir, \ + .mode = _mode, \ + .size = _size, \ + } + +#define MLXSW_SP_SB_POOL_INGRESS(_pool, _size) \ + MLXSW_SP_SB_POOL(_pool, MLXSW_REG_SBPR_DIR_INGRESS, \ + MLXSW_REG_SBPR_MODE_DYNAMIC, _size) + +#define MLXSW_SP_SB_POOL_EGRESS(_pool, _size) \ + MLXSW_SP_SB_POOL(_pool, MLXSW_REG_SBPR_DIR_EGRESS, \ + MLXSW_REG_SBPR_MODE_DYNAMIC, _size) + +static const struct mlxsw_sp_sb_pool mlxsw_sp_sb_pools[] = { + MLXSW_SP_SB_POOL_INGRESS(0, MLXSW_SP_SB_POOL_INGRESS_SIZE), + MLXSW_SP_SB_POOL_INGRESS(1, 0), + MLXSW_SP_SB_POOL_INGRESS(2, 0), + MLXSW_SP_SB_POOL_INGRESS(3, 0), + MLXSW_SP_SB_POOL_EGRESS(0, MLXSW_SP_SB_POOL_EGRESS_SIZE), + MLXSW_SP_SB_POOL_EGRESS(1, 0), + MLXSW_SP_SB_POOL_EGRESS(2, 0), + MLXSW_SP_SB_POOL_EGRESS(2, MLXSW_SP_SB_POOL_EGRESS_SIZE), +}; + +#define MLXSW_SP_SB_POOLS_LEN ARRAY_SIZE(mlxsw_sp_sb_pools) + +static int mlxsw_sp_sb_pools_init(struct mlxsw_sp *mlxsw_sp) +{ + char sbpr_pl[MLXSW_REG_SBPR_LEN]; + int i; + int err; + + for (i = 0; i < MLXSW_SP_SB_POOLS_LEN; i++) { + const struct mlxsw_sp_sb_pool *pool; + + pool = &mlxsw_sp_sb_pools[i]; + mlxsw_reg_sbpr_pack(sbpr_pl, pool->pool, pool->dir, + pool->mode, pool->size); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbpr), sbpr_pl); + if (err) + return err; + } + return 0; +} + +struct mlxsw_sp_sb_cm { + union { + u8 pg; + u8 tc; + } u; + enum mlxsw_reg_sbcm_dir dir; + u32 min_buff; + u32 max_buff; + u8 pool; +}; + +#define MLXSW_SP_SB_CM(_pg_tc, _dir, _min_buff, _max_buff, _pool) \ + { \ + .u.pg = _pg_tc, \ + .dir = _dir, \ + .min_buff = _min_buff, \ + .max_buff = _max_buff, \ + .pool = _pool, \ + } + +#define MLXSW_SP_SB_CM_INGRESS(_pg, _min_buff, _max_buff) \ + MLXSW_SP_SB_CM(_pg, MLXSW_REG_SBCM_DIR_INGRESS, \ + _min_buff, _max_buff, 0) + +#define MLXSW_SP_SB_CM_EGRESS(_tc, _min_buff, _max_buff) \ + MLXSW_SP_SB_CM(_tc, MLXSW_REG_SBCM_DIR_EGRESS, \ + _min_buff, _max_buff, 0) + +#define MLXSW_SP_CPU_PORT_SB_CM_EGRESS(_tc) \ + MLXSW_SP_SB_CM(_tc, MLXSW_REG_SBCM_DIR_EGRESS, 104, 2, 3) + +static const struct mlxsw_sp_sb_cm mlxsw_sp_sb_cms[] = { + MLXSW_SP_SB_CM_INGRESS(0, 10000 / MLXSW_SP_SB_BYTES_PER_CELL, 8), + MLXSW_SP_SB_CM_INGRESS(1, 0, 0), + MLXSW_SP_SB_CM_INGRESS(2, 0, 0), + MLXSW_SP_SB_CM_INGRESS(3, 0, 0), + MLXSW_SP_SB_CM_INGRESS(4, 0, 0), + MLXSW_SP_SB_CM_INGRESS(5, 0, 0), + MLXSW_SP_SB_CM_INGRESS(6, 0, 0), + MLXSW_SP_SB_CM_INGRESS(7, 0, 0), + MLXSW_SP_SB_CM_INGRESS(9, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff), + MLXSW_SP_SB_CM_EGRESS(0, 1500 / MLXSW_SP_SB_BYTES_PER_CELL, 9), + MLXSW_SP_SB_CM_EGRESS(1, 1500 / MLXSW_SP_SB_BYTES_PER_CELL, 9), + MLXSW_SP_SB_CM_EGRESS(2, 1500 / MLXSW_SP_SB_BYTES_PER_CELL, 9), + MLXSW_SP_SB_CM_EGRESS(3, 1500 / MLXSW_SP_SB_BYTES_PER_CELL, 9), + MLXSW_SP_SB_CM_EGRESS(4, 1500 / MLXSW_SP_SB_BYTES_PER_CELL, 9), + MLXSW_SP_SB_CM_EGRESS(5, 1500 / MLXSW_SP_SB_BYTES_PER_CELL, 9), + MLXSW_SP_SB_CM_EGRESS(6, 1500 / MLXSW_SP_SB_BYTES_PER_CELL, 9), + MLXSW_SP_SB_CM_EGRESS(7, 1500 / MLXSW_SP_SB_BYTES_PER_CELL, 9), + MLXSW_SP_SB_CM_EGRESS(8, 0, 0), + MLXSW_SP_SB_CM_EGRESS(9, 0, 0), + MLXSW_SP_SB_CM_EGRESS(10, 0, 0), + MLXSW_SP_SB_CM_EGRESS(11, 0, 0), + MLXSW_SP_SB_CM_EGRESS(12, 0, 0), + MLXSW_SP_SB_CM_EGRESS(13, 0, 0), + MLXSW_SP_SB_CM_EGRESS(14, 0, 0), + MLXSW_SP_SB_CM_EGRESS(15, 0, 0), + MLXSW_SP_SB_CM_EGRESS(16, 1, 0xff), +}; + +#define MLXSW_SP_SB_CMS_LEN ARRAY_SIZE(mlxsw_sp_sb_cms) + +static const struct mlxsw_sp_sb_cm mlxsw_sp_cpu_port_sb_cms[] = { + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(0), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(1), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(2), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(3), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(4), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(5), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(6), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(7), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(8), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(9), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(10), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(11), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(12), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(13), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(14), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(15), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(16), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(17), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(18), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(19), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(20), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(21), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(22), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(23), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(24), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(25), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(26), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(27), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(28), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(29), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(30), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(31), +}; + +#define MLXSW_SP_CPU_PORT_SB_MCS_LEN \ + ARRAY_SIZE(mlxsw_sp_cpu_port_sb_cms) + +static int mlxsw_sp_sb_cms_init(struct mlxsw_sp *mlxsw_sp, u8 local_port, + const struct mlxsw_sp_sb_cm *cms, + size_t cms_len) +{ + char sbcm_pl[MLXSW_REG_SBCM_LEN]; + int i; + int err; + + for (i = 0; i < cms_len; i++) { + const struct mlxsw_sp_sb_cm *cm; + + cm = &cms[i]; + mlxsw_reg_sbcm_pack(sbcm_pl, local_port, cm->u.pg, cm->dir, + cm->min_buff, cm->max_buff, cm->pool); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbcm), sbcm_pl); + if (err) + return err; + } + return 0; +} + +static int mlxsw_sp_port_sb_cms_init(struct mlxsw_sp_port *mlxsw_sp_port) +{ + return mlxsw_sp_sb_cms_init(mlxsw_sp_port->mlxsw_sp, + mlxsw_sp_port->local_port, mlxsw_sp_sb_cms, + MLXSW_SP_SB_CMS_LEN); +} + +static int mlxsw_sp_cpu_port_sb_cms_init(struct mlxsw_sp *mlxsw_sp) +{ + return mlxsw_sp_sb_cms_init(mlxsw_sp, 0, mlxsw_sp_cpu_port_sb_cms, + MLXSW_SP_CPU_PORT_SB_MCS_LEN); +} + +struct mlxsw_sp_sb_pm { + u8 pool; + enum mlxsw_reg_sbpm_dir dir; + u32 min_buff; + u32 max_buff; +}; + +#define MLXSW_SP_SB_PM(_pool, _dir, _min_buff, _max_buff) \ + { \ + .pool = _pool, \ + .dir = _dir, \ + .min_buff = _min_buff, \ + .max_buff = _max_buff, \ + } + +#define MLXSW_SP_SB_PM_INGRESS(_pool, _min_buff, _max_buff) \ + MLXSW_SP_SB_PM(_pool, MLXSW_REG_SBPM_DIR_INGRESS, \ + _min_buff, _max_buff) + +#define MLXSW_SP_SB_PM_EGRESS(_pool, _min_buff, _max_buff) \ + MLXSW_SP_SB_PM(_pool, MLXSW_REG_SBPM_DIR_EGRESS, \ + _min_buff, _max_buff) + +static const struct mlxsw_sp_sb_pm mlxsw_sp_sb_pms[] = { + MLXSW_SP_SB_PM_INGRESS(0, 0, 0xff), + MLXSW_SP_SB_PM_INGRESS(1, 0, 0), + MLXSW_SP_SB_PM_INGRESS(2, 0, 0), + MLXSW_SP_SB_PM_INGRESS(3, 0, 0), + MLXSW_SP_SB_PM_EGRESS(0, 0, 7), + MLXSW_SP_SB_PM_EGRESS(1, 0, 0), + MLXSW_SP_SB_PM_EGRESS(2, 0, 0), + MLXSW_SP_SB_PM_EGRESS(3, 0, 0), +}; + +#define MLXSW_SP_SB_PMS_LEN ARRAY_SIZE(mlxsw_sp_sb_pms) + +static int mlxsw_sp_port_sb_pms_init(struct mlxsw_sp_port *mlxsw_sp_port) +{ + char sbpm_pl[MLXSW_REG_SBPM_LEN]; + int i; + int err; + + for (i = 0; i < MLXSW_SP_SB_PMS_LEN; i++) { + const struct mlxsw_sp_sb_pm *pm; + + pm = &mlxsw_sp_sb_pms[i]; + mlxsw_reg_sbpm_pack(sbpm_pl, mlxsw_sp_port->local_port, + pm->pool, pm->dir, + pm->min_buff, pm->max_buff); + err = mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, + MLXSW_REG(sbpm), sbpm_pl); + if (err) + return err; + } + return 0; +} + +struct mlxsw_sp_sb_mm { + u8 prio; + u32 min_buff; + u32 max_buff; + u8 pool; +}; + +#define MLXSW_SP_SB_MM(_prio, _min_buff, _max_buff, _pool) \ + { \ + .prio = _prio, \ + .min_buff = _min_buff, \ + .max_buff = _max_buff, \ + .pool = _pool, \ + } + +static const struct mlxsw_sp_sb_mm mlxsw_sp_sb_mms[] = { + MLXSW_SP_SB_MM(0, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(1, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(2, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(3, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(4, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(5, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(6, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(7, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(8, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(9, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(10, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(11, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(12, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(13, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(14, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), +}; + +#define MLXSW_SP_SB_MMS_LEN ARRAY_SIZE(mlxsw_sp_sb_mms) + +static int mlxsw_sp_sb_mms_init(struct mlxsw_sp *mlxsw_sp) +{ + char sbmm_pl[MLXSW_REG_SBMM_LEN]; + int i; + int err; + + for (i = 0; i < MLXSW_SP_SB_MMS_LEN; i++) { + const struct mlxsw_sp_sb_mm *mc; + + mc = &mlxsw_sp_sb_mms[i]; + mlxsw_reg_sbmm_pack(sbmm_pl, mc->prio, mc->min_buff, + mc->max_buff, mc->pool); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbmm), sbmm_pl); + if (err) + return err; + } + return 0; +} + +int mlxsw_sp_buffers_init(struct mlxsw_sp *mlxsw_sp) +{ + int err; + + err = mlxsw_sp_sb_pools_init(mlxsw_sp); + if (err) + return err; + err = mlxsw_sp_cpu_port_sb_cms_init(mlxsw_sp); + if (err) + return err; + err = mlxsw_sp_sb_mms_init(mlxsw_sp); + + return err; +} + +int mlxsw_sp_port_buffers_init(struct mlxsw_sp_port *mlxsw_sp_port) +{ + int err; + + err = mlxsw_sp_port_pb_init(mlxsw_sp_port); + if (err) + return err; + err = mlxsw_sp_port_sb_cms_init(mlxsw_sp_port); + if (err) + return err; + err = mlxsw_sp_port_sb_pms_init(mlxsw_sp_port); + + return err; +} diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c new file mode 100644 index 0000000..c39b7a1 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -0,0 +1,863 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c + * Copyright (c) 2015 Mellanox Technologies. All rights reserved. + * Copyright (c) 2015 Jiri Pirko + * Copyright (c) 2015 Ido Schimmel + * Copyright (c) 2015 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 "spectrum.h" +#include "core.h" +#include "reg.h" + +static int mlxsw_sp_port_attr_get(struct net_device *dev, + struct switchdev_attr *attr) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + + switch (attr->id) { + case SWITCHDEV_ATTR_ID_PORT_PARENT_ID: + attr->u.ppid.id_len = sizeof(mlxsw_sp->base_mac); + memcpy(&attr->u.ppid.id, &mlxsw_sp->base_mac, + attr->u.ppid.id_len); + break; + case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: + attr->u.brport_flags = + (mlxsw_sp_port->learning ? BR_LEARNING : 0) | + (mlxsw_sp_port->learning_sync ? BR_LEARNING_SYNC : 0); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int mlxsw_sp_port_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port, + u8 state) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + enum mlxsw_reg_spms_state spms_state; + char *spms_pl; + u16 vid; + int err; + + switch (state) { + case BR_STATE_DISABLED: /* fall-through */ + case BR_STATE_FORWARDING: + spms_state = MLXSW_REG_SPMS_STATE_FORWARDING; + break; + case BR_STATE_LISTENING: /* fall-through */ + case BR_STATE_LEARNING: + spms_state = MLXSW_REG_SPMS_STATE_LEARNING; + break; + case BR_STATE_BLOCKING: + spms_state = MLXSW_REG_SPMS_STATE_DISCARDING; + break; + default: + BUG(); + } + + spms_pl = kmalloc(MLXSW_REG_SPMS_LEN, GFP_KERNEL); + if (!spms_pl) + return -ENOMEM; + mlxsw_reg_spms_pack(spms_pl, mlxsw_sp_port->local_port); + for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) + mlxsw_reg_spms_vid_pack(spms_pl, vid, spms_state); + + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spms), spms_pl); + kfree(spms_pl); + return err; +} + +static int mlxsw_sp_port_attr_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port, + struct switchdev_trans *trans, + u8 state) +{ + if (switchdev_trans_ph_prepare(trans)) + return 0; + + mlxsw_sp_port->stp_state = state; + return mlxsw_sp_port_stp_state_set(mlxsw_sp_port, state); +} + +static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port, + struct switchdev_trans *trans, + unsigned long brport_flags) +{ + if (switchdev_trans_ph_prepare(trans)) + return 0; + + mlxsw_sp_port->learning = brport_flags & BR_LEARNING ? 1 : 0; + mlxsw_sp_port->learning_sync = brport_flags & BR_LEARNING_SYNC ? 1 : 0; + return 0; +} + +static int mlxsw_sp_ageing_set(struct mlxsw_sp *mlxsw_sp, u32 ageing_time) +{ + char sfdat_pl[MLXSW_REG_SFDAT_LEN]; + int err; + + mlxsw_reg_sfdat_pack(sfdat_pl, ageing_time); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfdat), sfdat_pl); + if (err) + return err; + mlxsw_sp->ageing_time = ageing_time; + return 0; +} + +static int mlxsw_sp_port_attr_br_ageing_set(struct mlxsw_sp_port *mlxsw_sp_port, + struct switchdev_trans *trans, + unsigned long ageing_jiffies) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + u32 ageing_time = jiffies_to_msecs(ageing_jiffies) / 1000; + + if (switchdev_trans_ph_prepare(trans)) + return 0; + + return mlxsw_sp_ageing_set(mlxsw_sp, ageing_time); +} + +static int mlxsw_sp_port_attr_set(struct net_device *dev, + const struct switchdev_attr *attr, + struct switchdev_trans *trans) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + int err = 0; + + switch (attr->id) { + case SWITCHDEV_ATTR_ID_PORT_STP_STATE: + err = mlxsw_sp_port_attr_stp_state_set(mlxsw_sp_port, trans, + attr->u.stp_state); + break; + case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: + err = mlxsw_sp_port_attr_br_flags_set(mlxsw_sp_port, trans, + attr->u.brport_flags); + break; + case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: + err = mlxsw_sp_port_attr_br_ageing_set(mlxsw_sp_port, trans, + attr->u.ageing_time); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +static int mlxsw_sp_port_pvid_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char spvid_pl[MLXSW_REG_SPVID_LEN]; + + mlxsw_reg_spvid_pack(spvid_pl, mlxsw_sp_port->local_port, vid); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spvid), spvid_pl); +} + +static int mlxsw_sp_fid_create(struct mlxsw_sp *mlxsw_sp, u16 fid) +{ + char sfmr_pl[MLXSW_REG_SFMR_LEN]; + int err; + + mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_CREATE_FID, fid, fid); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl); + + if (err) + return err; + + set_bit(fid, mlxsw_sp->active_fids); + return 0; +} + +static void mlxsw_sp_fid_destroy(struct mlxsw_sp *mlxsw_sp, u16 fid) +{ + char sfmr_pl[MLXSW_REG_SFMR_LEN]; + + clear_bit(fid, mlxsw_sp->active_fids); + + mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_DESTROY_FID, + fid, fid); + mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl); +} + +static int mlxsw_sp_port_fid_map(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid) +{ + enum mlxsw_reg_svfa_mt mt; + + if (mlxsw_sp_port->nr_vfids) + mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID; + else + mt = MLXSW_REG_SVFA_MT_VID_TO_FID; + + return mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, mt, true, fid, fid); +} + +static int mlxsw_sp_port_fid_unmap(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid) +{ + enum mlxsw_reg_svfa_mt mt; + + if (!mlxsw_sp_port->nr_vfids) + return 0; + + mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID; + return mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, mt, false, fid, fid); +} + +static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port, + u16 fid, bool set, bool only_uc) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char *sftr_pl; + int err; + + sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL); + if (!sftr_pl) + return -ENOMEM; + + mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_UC, fid, + MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST, 0, + mlxsw_sp_port->local_port, set); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl); + if (err) + goto buffer_out; + + /* Flooding control allows one to decide whether a given port will + * flood unicast traffic for which there is no FDB entry. + */ + if (only_uc) + goto buffer_out; + + mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BM, fid, + MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST, 0, + mlxsw_sp_port->local_port, set); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl); + +buffer_out: + kfree(sftr_pl); + return err; +} + +static int mlxsw_sp_port_add_vids(struct net_device *dev, u16 vid_begin, + u16 vid_end) +{ + u16 vid; + int err; + + for (vid = vid_begin; vid <= vid_end; vid++) { + err = mlxsw_sp_port_add_vid(dev, 0, vid); + if (err) + goto err_port_add_vid; + } + return 0; + +err_port_add_vid: + for (vid--; vid >= vid_begin; vid--) + mlxsw_sp_port_kill_vid(dev, 0, vid); + return err; +} + +static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, + u16 vid_begin, u16 vid_end, + bool flag_untagged, bool flag_pvid) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + struct net_device *dev = mlxsw_sp_port->dev; + enum mlxsw_reg_svfa_mt mt; + u16 vid, vid_e; + int err; + + /* In case this is invoked with BRIDGE_FLAGS_SELF and port is + * not bridged, then packets ingressing through the port with + * the specified VIDs will be directed to CPU. + */ + if (!mlxsw_sp_port->bridged) + return mlxsw_sp_port_add_vids(dev, vid_begin, vid_end); + + for (vid = vid_begin; vid <= vid_end; vid++) { + if (!test_bit(vid, mlxsw_sp->active_fids)) { + err = mlxsw_sp_fid_create(mlxsw_sp, vid); + if (err) { + netdev_err(dev, "Failed to create FID=%d\n", + vid); + return err; + } + + /* When creating a FID, we set a VID to FID mapping + * regardless of the port's mode. + */ + mt = MLXSW_REG_SVFA_MT_VID_TO_FID; + err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, mt, + true, vid, vid); + if (err) { + netdev_err(dev, "Failed to create FID=VID=%d mapping\n", + vid); + return err; + } + } + + /* Set FID mapping according to port's mode */ + err = mlxsw_sp_port_fid_map(mlxsw_sp_port, vid); + if (err) { + netdev_err(dev, "Failed to map FID=%d", vid); + return err; + } + + err = __mlxsw_sp_port_flood_set(mlxsw_sp_port, vid, true, + false); + if (err) { + netdev_err(dev, "Failed to set flooding for FID=%d", + vid); + return err; + } + } + + for (vid = vid_begin; vid <= vid_end; + vid += MLXSW_REG_SPVM_REC_MAX_COUNT) { + vid_e = min((u16) (vid + MLXSW_REG_SPVM_REC_MAX_COUNT - 1), + vid_end); + + err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid_e, true, + flag_untagged); + if (err) { + netdev_err(mlxsw_sp_port->dev, "Unable to add VIDs %d-%d\n", + vid, vid_e); + return err; + } + } + + vid = vid_begin; + if (flag_pvid && mlxsw_sp_port->pvid != vid) { + err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, vid); + if (err) { + netdev_err(mlxsw_sp_port->dev, "Unable to add PVID %d\n", + vid); + return err; + } + mlxsw_sp_port->pvid = vid; + } + + /* Changing activity bits only if HW operation succeded */ + for (vid = vid_begin; vid <= vid_end; vid++) + set_bit(vid, mlxsw_sp_port->active_vlans); + + return mlxsw_sp_port_stp_state_set(mlxsw_sp_port, + mlxsw_sp_port->stp_state); +} + +static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, + const struct switchdev_obj_port_vlan *vlan, + struct switchdev_trans *trans) +{ + bool untagged_flag = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; + bool pvid_flag = vlan->flags & BRIDGE_VLAN_INFO_PVID; + + if (switchdev_trans_ph_prepare(trans)) + return 0; + + return __mlxsw_sp_port_vlans_add(mlxsw_sp_port, + vlan->vid_begin, vlan->vid_end, + untagged_flag, pvid_flag); +} + +static int mlxsw_sp_port_fdb_op(struct mlxsw_sp_port *mlxsw_sp_port, + const char *mac, u16 vid, bool adding, + bool dynamic) +{ + enum mlxsw_reg_sfd_rec_policy policy; + enum mlxsw_reg_sfd_op op; + char *sfd_pl; + int err; + + if (!vid) + vid = mlxsw_sp_port->pvid; + + sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL); + if (!sfd_pl) + return -ENOMEM; + + policy = dynamic ? MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_INGRESS : + MLXSW_REG_SFD_REC_POLICY_STATIC_ENTRY; + op = adding ? MLXSW_REG_SFD_OP_WRITE_EDIT : + MLXSW_REG_SFD_OP_WRITE_REMOVE; + mlxsw_reg_sfd_pack(sfd_pl, op, 0); + mlxsw_reg_sfd_uc_pack(sfd_pl, 0, policy, + mac, vid, MLXSW_REG_SFD_REC_ACTION_NOP, + mlxsw_sp_port->local_port); + err = mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(sfd), + sfd_pl); + kfree(sfd_pl); + + return err; +} + +static int +mlxsw_sp_port_fdb_static_add(struct mlxsw_sp_port *mlxsw_sp_port, + const struct switchdev_obj_port_fdb *fdb, + struct switchdev_trans *trans) +{ + if (switchdev_trans_ph_prepare(trans)) + return 0; + + return mlxsw_sp_port_fdb_op(mlxsw_sp_port, fdb->addr, fdb->vid, + true, false); +} + +static int mlxsw_sp_port_obj_add(struct net_device *dev, + const struct switchdev_obj *obj, + struct switchdev_trans *trans) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + int err = 0; + + switch (obj->id) { + case SWITCHDEV_OBJ_ID_PORT_VLAN: + err = mlxsw_sp_port_vlans_add(mlxsw_sp_port, + SWITCHDEV_OBJ_PORT_VLAN(obj), + trans); + break; + case SWITCHDEV_OBJ_ID_PORT_FDB: + err = mlxsw_sp_port_fdb_static_add(mlxsw_sp_port, + SWITCHDEV_OBJ_PORT_FDB(obj), + trans); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +static int mlxsw_sp_port_kill_vids(struct net_device *dev, u16 vid_begin, + u16 vid_end) +{ + u16 vid; + int err; + + for (vid = vid_begin; vid <= vid_end; vid++) { + err = mlxsw_sp_port_kill_vid(dev, 0, vid); + if (err) + return err; + } + + return 0; +} + +static int __mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port, + u16 vid_begin, u16 vid_end, bool init) +{ + struct net_device *dev = mlxsw_sp_port->dev; + u16 vid, vid_e; + int err; + + /* In case this is invoked with BRIDGE_FLAGS_SELF and port is + * not bridged, then prevent packets ingressing through the + * port with the specified VIDs from being trapped to CPU. + */ + if (!init && !mlxsw_sp_port->bridged) + return mlxsw_sp_port_kill_vids(dev, vid_begin, vid_end); + + for (vid = vid_begin; vid <= vid_end; + vid += MLXSW_REG_SPVM_REC_MAX_COUNT) { + vid_e = min((u16) (vid + MLXSW_REG_SPVM_REC_MAX_COUNT - 1), + vid_end); + err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid_e, false, + false); + if (err) { + netdev_err(mlxsw_sp_port->dev, "Unable to del VIDs %d-%d\n", + vid, vid_e); + return err; + } + } + + if ((mlxsw_sp_port->pvid >= vid_begin) && + (mlxsw_sp_port->pvid <= vid_end)) { + /* Default VLAN is always 1 */ + mlxsw_sp_port->pvid = 1; + err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, + mlxsw_sp_port->pvid); + if (err) { + netdev_err(mlxsw_sp_port->dev, "Unable to del PVID %d\n", + vid); + return err; + } + } + + if (init) + goto out; + + for (vid = vid_begin; vid <= vid_end; vid++) { + err = __mlxsw_sp_port_flood_set(mlxsw_sp_port, vid, false, + false); + if (err) { + netdev_err(dev, "Failed to clear flooding for FID=%d", + vid); + return err; + } + + /* Remove FID mapping in case of Virtual mode */ + err = mlxsw_sp_port_fid_unmap(mlxsw_sp_port, vid); + if (err) { + netdev_err(dev, "Failed to unmap FID=%d", vid); + return err; + } + } + +out: + /* Changing activity bits only if HW operation succeded */ + for (vid = vid_begin; vid <= vid_end; vid++) + clear_bit(vid, mlxsw_sp_port->active_vlans); + + return 0; +} + +static int mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port, + const struct switchdev_obj_port_vlan *vlan) +{ + return __mlxsw_sp_port_vlans_del(mlxsw_sp_port, + vlan->vid_begin, vlan->vid_end, false); +} + +static int +mlxsw_sp_port_fdb_static_del(struct mlxsw_sp_port *mlxsw_sp_port, + const struct switchdev_obj_port_fdb *fdb) +{ + return mlxsw_sp_port_fdb_op(mlxsw_sp_port, fdb->addr, fdb->vid, + false, false); +} + +static int mlxsw_sp_port_obj_del(struct net_device *dev, + const struct switchdev_obj *obj) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + int err = 0; + + switch (obj->id) { + case SWITCHDEV_OBJ_ID_PORT_VLAN: + err = mlxsw_sp_port_vlans_del(mlxsw_sp_port, + SWITCHDEV_OBJ_PORT_VLAN(obj)); + break; + case SWITCHDEV_OBJ_ID_PORT_FDB: + err = mlxsw_sp_port_fdb_static_del(mlxsw_sp_port, + SWITCHDEV_OBJ_PORT_FDB(obj)); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port, + struct switchdev_obj_port_fdb *fdb, + switchdev_obj_dump_cb_t *cb) +{ + char *sfd_pl; + char mac[ETH_ALEN]; + u16 vid; + u8 local_port; + u8 num_rec; + int stored_err = 0; + int i; + int err; + + sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL); + if (!sfd_pl) + return -ENOMEM; + + mlxsw_reg_sfd_pack(sfd_pl, MLXSW_REG_SFD_OP_QUERY_DUMP, 0); + do { + mlxsw_reg_sfd_num_rec_set(sfd_pl, MLXSW_REG_SFD_REC_MAX_COUNT); + err = mlxsw_reg_query(mlxsw_sp_port->mlxsw_sp->core, + MLXSW_REG(sfd), sfd_pl); + if (err) + goto out; + + num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl); + + /* Even in case of error, we have to run the dump to the end + * so the session in firmware is finished. + */ + if (stored_err) + continue; + + for (i = 0; i < num_rec; i++) { + switch (mlxsw_reg_sfd_rec_type_get(sfd_pl, i)) { + case MLXSW_REG_SFD_REC_TYPE_UNICAST: + mlxsw_reg_sfd_uc_unpack(sfd_pl, i, mac, &vid, + &local_port); + if (local_port == mlxsw_sp_port->local_port) { + ether_addr_copy(fdb->addr, mac); + fdb->ndm_state = NUD_REACHABLE; + fdb->vid = vid; + err = cb(&fdb->obj); + if (err) + stored_err = err; + } + } + } + } while (num_rec == MLXSW_REG_SFD_REC_MAX_COUNT); + +out: + kfree(sfd_pl); + return stored_err ? stored_err : err; +} + +static int mlxsw_sp_port_vlan_dump(struct mlxsw_sp_port *mlxsw_sp_port, + struct switchdev_obj_port_vlan *vlan, + switchdev_obj_dump_cb_t *cb) +{ + u16 vid; + int err = 0; + + for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) { + vlan->flags = 0; + if (vid == mlxsw_sp_port->pvid) + vlan->flags |= BRIDGE_VLAN_INFO_PVID; + vlan->vid_begin = vid; + vlan->vid_end = vid; + err = cb(&vlan->obj); + if (err) + break; + } + return err; +} + +static int mlxsw_sp_port_obj_dump(struct net_device *dev, + struct switchdev_obj *obj, + switchdev_obj_dump_cb_t *cb) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + int err = 0; + + switch (obj->id) { + case SWITCHDEV_OBJ_ID_PORT_VLAN: + err = mlxsw_sp_port_vlan_dump(mlxsw_sp_port, + SWITCHDEV_OBJ_PORT_VLAN(obj), cb); + break; + case SWITCHDEV_OBJ_ID_PORT_FDB: + err = mlxsw_sp_port_fdb_dump(mlxsw_sp_port, + SWITCHDEV_OBJ_PORT_FDB(obj), cb); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +const struct switchdev_ops mlxsw_sp_port_switchdev_ops = { + .switchdev_port_attr_get = mlxsw_sp_port_attr_get, + .switchdev_port_attr_set = mlxsw_sp_port_attr_set, + .switchdev_port_obj_add = mlxsw_sp_port_obj_add, + .switchdev_port_obj_del = mlxsw_sp_port_obj_del, + .switchdev_port_obj_dump = mlxsw_sp_port_obj_dump, +}; + +static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp, + char *sfn_pl, int rec_index, + bool adding) +{ + struct mlxsw_sp_port *mlxsw_sp_port; + char mac[ETH_ALEN]; + u8 local_port; + u16 vid; + int err; + + mlxsw_reg_sfn_mac_unpack(sfn_pl, rec_index, mac, &vid, &local_port); + mlxsw_sp_port = mlxsw_sp->ports[local_port]; + if (!mlxsw_sp_port) { + dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect local port in FDB notification\n"); + return; + } + + err = mlxsw_sp_port_fdb_op(mlxsw_sp_port, mac, vid, + adding && mlxsw_sp_port->learning, true); + if (err) { + if (net_ratelimit()) + netdev_err(mlxsw_sp_port->dev, "Failed to set FDB entry\n"); + return; + } + + if (mlxsw_sp_port->learning && mlxsw_sp_port->learning_sync) { + struct switchdev_notifier_fdb_info info; + unsigned long notifier_type; + + info.addr = mac; + info.vid = vid; + notifier_type = adding ? SWITCHDEV_FDB_ADD : SWITCHDEV_FDB_DEL; + call_switchdev_notifiers(notifier_type, mlxsw_sp_port->dev, + &info.info); + } +} + +static void mlxsw_sp_fdb_notify_rec_process(struct mlxsw_sp *mlxsw_sp, + char *sfn_pl, int rec_index) +{ + switch (mlxsw_reg_sfn_rec_type_get(sfn_pl, rec_index)) { + case MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC: + mlxsw_sp_fdb_notify_mac_process(mlxsw_sp, sfn_pl, + rec_index, true); + break; + case MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC: + mlxsw_sp_fdb_notify_mac_process(mlxsw_sp, sfn_pl, + rec_index, false); + break; + } +} + +static void mlxsw_sp_fdb_notify_work_schedule(struct mlxsw_sp *mlxsw_sp) +{ + schedule_delayed_work(&mlxsw_sp->fdb_notify.dw, + msecs_to_jiffies(mlxsw_sp->fdb_notify.interval)); +} + +static void mlxsw_sp_fdb_notify_work(struct work_struct *work) +{ + struct mlxsw_sp *mlxsw_sp; + char *sfn_pl; + u8 num_rec; + int i; + int err; + + sfn_pl = kmalloc(MLXSW_REG_SFN_LEN, GFP_KERNEL); + if (!sfn_pl) + return; + + mlxsw_sp = container_of(work, struct mlxsw_sp, fdb_notify.dw.work); + + do { + mlxsw_reg_sfn_pack(sfn_pl); + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(sfn), sfn_pl); + if (err) { + dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to get FDB notifications\n"); + break; + } + num_rec = mlxsw_reg_sfn_num_rec_get(sfn_pl); + for (i = 0; i < num_rec; i++) + mlxsw_sp_fdb_notify_rec_process(mlxsw_sp, sfn_pl, i); + + } while (num_rec); + + kfree(sfn_pl); + mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp); +} + +static int mlxsw_sp_fdb_init(struct mlxsw_sp *mlxsw_sp) +{ + int err; + + err = mlxsw_sp_ageing_set(mlxsw_sp, MLXSW_SP_DEFAULT_AGEING_TIME); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Failed to set default ageing time\n"); + return err; + } + INIT_DELAYED_WORK(&mlxsw_sp->fdb_notify.dw, mlxsw_sp_fdb_notify_work); + mlxsw_sp->fdb_notify.interval = MLXSW_SP_DEFAULT_LEARNING_INTERVAL; + mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp); + return 0; +} + +static void mlxsw_sp_fdb_fini(struct mlxsw_sp *mlxsw_sp) +{ + cancel_delayed_work_sync(&mlxsw_sp->fdb_notify.dw); +} + +static void mlxsw_sp_fids_fini(struct mlxsw_sp *mlxsw_sp) +{ + u16 fid; + + for_each_set_bit(fid, mlxsw_sp->active_fids, VLAN_N_VID) + mlxsw_sp_fid_destroy(mlxsw_sp, fid); +} + +int mlxsw_sp_switchdev_init(struct mlxsw_sp *mlxsw_sp) +{ + return mlxsw_sp_fdb_init(mlxsw_sp); +} + +void mlxsw_sp_switchdev_fini(struct mlxsw_sp *mlxsw_sp) +{ + mlxsw_sp_fdb_fini(mlxsw_sp); + mlxsw_sp_fids_fini(mlxsw_sp); +} + +int mlxsw_sp_port_vlan_init(struct mlxsw_sp_port *mlxsw_sp_port) +{ + struct net_device *dev = mlxsw_sp_port->dev; + int err; + + /* Allow only untagged packets to ingress and tag them internally + * with VID 1. + */ + mlxsw_sp_port->pvid = 1; + err = __mlxsw_sp_port_vlans_del(mlxsw_sp_port, 0, VLAN_N_VID, true); + if (err) { + netdev_err(dev, "Unable to init VLANs\n"); + return err; + } + + /* Add implicit VLAN interface in the device, so that untagged + * packets will be classified to the default vFID. + */ + err = mlxsw_sp_port_add_vid(dev, 0, 1); + if (err) + netdev_err(dev, "Failed to configure default vFID\n"); + + return err; +} + +void mlxsw_sp_port_switchdev_init(struct mlxsw_sp_port *mlxsw_sp_port) +{ + mlxsw_sp_port->dev->switchdev_ops = &mlxsw_sp_port_switchdev_ops; +} + +void mlxsw_sp_port_switchdev_fini(struct mlxsw_sp_port *mlxsw_sp_port) +{ +} diff --git a/drivers/net/ethernet/mellanox/mlxsw/txheader.h b/drivers/net/ethernet/mellanox/mlxsw/txheader.h index 06fc46c..fdf9472 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/txheader.h +++ b/drivers/net/ethernet/mellanox/mlxsw/txheader.h @@ -38,6 +38,7 @@ #define MLXSW_TXHDR_LEN 0x10 #define MLXSW_TXHDR_VERSION_0 0 +#define MLXSW_TXHDR_VERSION_1 1 enum { MLXSW_TXHDR_ETH_CTL, -- cgit v0.10.2